Skip to main content

irq_framework/
registry.rs

1use alloc::{boxed::Box, vec::Vec};
2use core::{
3    cell::UnsafeCell,
4    ptr,
5    sync::atomic::{AtomicU64, Ordering},
6};
7
8use crate::{
9    CpuId, IrqAffinity, IrqContext, IrqError, IrqExecution, IrqHandle, IrqId, IrqOps, IrqOutcome,
10    IrqRequest, IrqReturn, IrqScope, IrqStatus,
11    action::Action,
12    descriptor::{Descriptor, action_matches_cpu, recompute_scope_line_desired},
13    lock::MetadataLock,
14};
15
16/// Dynamic IRQ registry.
17pub struct Registry<O: IrqOps> {
18    ops: O,
19    lock: MetadataLock,
20    next_id: AtomicU64,
21    state: UnsafeCell<RegistryState>,
22}
23
24unsafe impl<O: IrqOps + Send> Send for Registry<O> {}
25unsafe impl<O: IrqOps + Send> Sync for Registry<O> {}
26
27struct RegistryState {
28    descriptors: Vec<Descriptor>,
29}
30
31impl RegistryState {
32    fn new() -> Self {
33        Self {
34            descriptors: Vec::new(),
35        }
36    }
37}
38
39impl<O: IrqOps> Registry<O> {
40    /// Creates an empty registry.
41    pub fn new(ops: O) -> Self {
42        Self {
43            ops,
44            lock: MetadataLock::new(),
45            next_id: AtomicU64::new(1),
46            state: UnsafeCell::new(RegistryState::new()),
47        }
48    }
49
50    /// Registers an IRQ action.
51    pub fn request(&self, irq: IrqId, mut request: IrqRequest) -> Result<IrqHandle, IrqError> {
52        self.validate_request(&request)?;
53
54        let snapshot = self.snapshot_and_disable_scope_line(irq, request.scope)?;
55        let id = self.next_id.fetch_add(1, Ordering::Relaxed);
56        let action = Box::new(Action::new(id, &mut request));
57        let action = Box::into_raw(action);
58        let irq_state = self.lock.lock(&self.ops);
59        let result = self.insert_action_locked(irq, &request, action);
60        self.lock.unlock(&self.ops, irq_state);
61
62        if let Err(err) = result {
63            unsafe {
64                drop(Box::from_raw(action));
65            }
66            let _ = self.restore_scope_line_snapshot(irq, request.scope, &snapshot);
67            return Err(err);
68        }
69
70        let handle = IrqHandle { irq, id };
71        if let Err(err) = self.apply_affinity(irq, request.affinity) {
72            self.drop_detached_action(handle);
73            let _ = self.restore_scope_line_snapshot(irq, request.scope, &snapshot);
74            return Err(err);
75        }
76        let restore_result = self.restore_scope_line_snapshot(irq, request.scope, &snapshot);
77        if let Err(err) = restore_result {
78            self.drop_detached_action(handle);
79            return Err(err);
80        }
81        Ok(handle)
82    }
83
84    /// Frees an IRQ action.
85    pub fn free(&self, handle: IrqHandle) -> Result<(), IrqError> {
86        if self.ops.in_irq_context() {
87            return Err(IrqError::InIrqContext);
88        }
89        let (action, scope) = self.detach_action(handle)?;
90        let mut result = self.apply_scope_line_state(handle.irq, scope);
91        if let Err(err) = self.wait_and_remove_action(handle.irq, action)
92            && result.is_ok()
93        {
94            result = Err(err);
95        }
96        unsafe {
97            drop(Box::from_raw(action));
98        }
99        result
100    }
101
102    /// Enables an IRQ action and its backing line.
103    pub fn enable(&self, handle: IrqHandle) -> Result<(), IrqError> {
104        let scope = self.set_action_enabled(handle, true)?;
105
106        if let Err(err) = self.apply_enabled(handle, scope, true) {
107            let _ = self.disable(handle);
108            return Err(err);
109        }
110        Ok(())
111    }
112
113    /// Disables an IRQ action and its backing line.
114    pub fn disable(&self, handle: IrqHandle) -> Result<(), IrqError> {
115        let scope = self.set_action_enabled(handle, false)?;
116        self.apply_enabled(handle, scope, false)
117    }
118
119    /// Waits until no handler is in flight for this IRQ descriptor.
120    pub fn synchronize(&self, handle: IrqHandle) -> Result<(), IrqError> {
121        if self.ops.in_irq_context() {
122            return Err(IrqError::InIrqContext);
123        }
124        loop {
125            let in_flight = self.with_action(handle, |_| {
126                self.descriptor(handle.irq)
127                    .map(|desc| desc.in_flight.load(Ordering::Acquire))
128                    .unwrap_or(0)
129            })?;
130            if in_flight == 0 {
131                return Ok(());
132            }
133            self.ops.relax();
134        }
135    }
136
137    fn set_action_enabled(&self, handle: IrqHandle, enabled: bool) -> Result<IrqScope, IrqError> {
138        let irq_state = self.lock.lock(&self.ops);
139        let result = (|| {
140            let state = unsafe { &mut *self.state.get() };
141            let descriptor = state
142                .descriptors
143                .iter_mut()
144                .find(|descriptor| descriptor.irq == handle.irq)
145                .ok_or(IrqError::NotFound)?;
146            let action = descriptor
147                .actions()
148                .find(|action| unsafe { (**action).id == handle.id })
149                .ok_or(IrqError::NotFound)?;
150            unsafe {
151                if (*action).detached.load(Ordering::Acquire) {
152                    return Err(IrqError::NotFound);
153                }
154                (*action).enabled.store(enabled, Ordering::Release);
155                (*action).clear_pending_enable_all();
156                let scope = (*action).scope;
157                recompute_scope_line_desired(descriptor, scope);
158                Ok(scope)
159            }
160        })();
161        self.lock.unlock(&self.ops, irq_state);
162        result
163    }
164
165    /// Returns a status snapshot for an IRQ action.
166    pub fn status(&self, handle: IrqHandle) -> Result<IrqStatus, IrqError> {
167        let (scope, action_enabled, in_flight) = self.with_action(handle, |action| {
168            let in_flight = self
169                .descriptor(handle.irq)
170                .map(|desc| desc.in_flight.load(Ordering::Acquire))
171                .unwrap_or(0);
172            (
173                action.scope,
174                action.enabled.load(Ordering::Acquire),
175                in_flight,
176            )
177        })?;
178        let action_running =
179            self.with_action(handle, |action| action.running.load(Ordering::Acquire))?;
180        let cpu = status_cpu(scope, self.ops.current_cpu());
181        let line_enabled = match self.ops.is_enabled(handle.irq, cpu) {
182            Ok(enabled) => enabled,
183            Err(IrqError::Unsupported) => self.framework_line_enabled(handle.irq, cpu)?,
184            Err(err) => return Err(err),
185        };
186        let pending = match self.ops.is_pending(handle.irq, cpu) {
187            Ok(pending) => pending,
188            Err(IrqError::Unsupported) => false,
189            Err(err) => return Err(err),
190        };
191        let in_service = match self.ops.is_in_service(handle.irq, cpu) {
192            Ok(in_service) => in_service,
193            Err(IrqError::Unsupported) => false,
194            Err(err) => return Err(err),
195        };
196        Ok(IrqStatus {
197            action_enabled,
198            line_enabled,
199            pending,
200            in_service,
201            in_flight,
202            action_running,
203        })
204    }
205
206    /// Dispatches an IRQ on the given CPU.
207    pub fn dispatch(&self, irq: IrqId, cpu: CpuId) -> IrqOutcome {
208        let Some(head) = self.begin_dispatch(irq) else {
209            return IrqOutcome::default();
210        };
211        let _guard = DispatchGuard {
212            registry: self,
213            irq,
214        };
215
216        let mut outcome = IrqOutcome::default();
217        let ctx = IrqContext { irq, cpu };
218        let mut next = head;
219        while !next.is_null() {
220            let action = unsafe { &*next };
221            next = action.next;
222            if action.detached.load(Ordering::Acquire)
223                || !action.enabled.load(Ordering::Acquire)
224                || !action_matches_cpu(action.scope, cpu)
225            {
226                continue;
227            }
228
229            let Some(_guard) = ActionRunGuard::enter(action) else {
230                continue;
231            };
232
233            outcome.called += 1;
234            match action.call(ctx) {
235                IrqReturn::Unhandled => {}
236                IrqReturn::Handled => outcome.handled = true,
237                IrqReturn::Wake => {
238                    outcome.handled = true;
239                    outcome.wake = true;
240                }
241            }
242        }
243
244        outcome
245    }
246
247    /// Marks a CPU online and applies pending per-CPU enables for that CPU.
248    pub fn cpu_online(&self, cpu: CpuId) -> Result<(), IrqError> {
249        if !self.ops.cpu_online(cpu) {
250            return Err(IrqError::CpuOffline);
251        }
252        let pending = self.pending_enables_for_cpu(cpu);
253        for irq in pending {
254            self.apply_line_state(irq, Some(cpu))?;
255            self.clear_pending_enable_for_cpu(irq, cpu);
256        }
257        Ok(())
258    }
259
260    /// Marks a CPU offline from the framework's perspective.
261    pub fn cpu_offline(&self, cpu: CpuId) -> Result<(), IrqError> {
262        if self.ops.cpu_online(cpu) {
263            return Err(IrqError::Unsupported);
264        }
265        Ok(())
266    }
267
268    fn validate_request(&self, request: &IrqRequest) -> Result<(), IrqError> {
269        if request.execution == IrqExecution::Concurrent && !request.supports_concurrent() {
270            return Err(IrqError::Busy);
271        }
272        if let IrqScope::PerCpu { cpus } = request.scope
273            && cpus.is_empty()
274        {
275            return Err(IrqError::InvalidCpu);
276        }
277        if let IrqAffinity::Fixed(cpu) = request.affinity
278            && !self.ops.cpu_online(cpu)
279        {
280            return Err(IrqError::CpuOffline);
281        }
282        Ok(())
283    }
284
285    fn insert_action_locked(
286        &self,
287        irq: IrqId,
288        request: &IrqRequest,
289        action: *mut Action,
290    ) -> Result<(), IrqError> {
291        let state = unsafe { &mut *self.state.get() };
292        let descriptor = match state
293            .descriptors
294            .iter_mut()
295            .find(|descriptor| descriptor.irq == irq)
296        {
297            Some(descriptor) => descriptor,
298            None => {
299                state.descriptors.push(Descriptor::new(irq, request));
300                state.descriptors.last_mut().ok_or(IrqError::NoMemory)?
301            }
302        };
303        descriptor.compatible_with(request)?;
304        unsafe {
305            (*action).next = descriptor.head;
306        }
307        descriptor.head = action;
308        recompute_scope_line_desired(descriptor, request.scope);
309        Ok(())
310    }
311
312    fn detach_action(&self, handle: IrqHandle) -> Result<(*mut Action, IrqScope), IrqError> {
313        let irq_state = self.lock.lock(&self.ops);
314        let result = (|| {
315            let state = unsafe { &mut *self.state.get() };
316            let descriptor = state
317                .descriptors
318                .iter_mut()
319                .find(|descriptor| descriptor.irq == handle.irq)
320                .ok_or(IrqError::NotFound)?;
321            let action = descriptor
322                .actions()
323                .find(|action| unsafe { (**action).id == handle.id })
324                .ok_or(IrqError::NotFound)?;
325            unsafe {
326                if (*action).detached.swap(true, Ordering::AcqRel) {
327                    return Err(IrqError::NotFound);
328                }
329                (*action).enabled.store(false, Ordering::Release);
330                (*action).clear_pending_enable_all();
331                let scope = (*action).scope;
332                recompute_scope_line_desired(descriptor, scope);
333                Ok((action, scope))
334            }
335        })();
336        self.lock.unlock(&self.ops, irq_state);
337        result
338    }
339
340    fn wait_and_remove_action(&self, irq: IrqId, action: *mut Action) -> Result<(), IrqError> {
341        loop {
342            match self.try_remove_action(irq, action) {
343                Err(IrqError::Busy) => self.ops.relax(),
344                result => return result,
345            }
346        }
347    }
348
349    fn try_remove_action(&self, irq: IrqId, action: *mut Action) -> Result<(), IrqError> {
350        let irq_state = self.lock.lock(&self.ops);
351        let result = (|| {
352            let state = unsafe { &mut *self.state.get() };
353            let descriptor = state
354                .descriptors
355                .iter_mut()
356                .find(|descriptor| descriptor.irq == irq)
357                .ok_or(IrqError::NotFound)?;
358            if descriptor.in_flight.load(Ordering::Acquire) != 0 {
359                return Err(IrqError::Busy);
360            }
361            let mut link = &mut descriptor.head as *mut *mut Action;
362            while unsafe { !(*link).is_null() } {
363                let current = unsafe { *link };
364                if current == action {
365                    unsafe {
366                        *link = (*current).next;
367                        (*current).next = ptr::null_mut();
368                    }
369                    return Ok(());
370                }
371                link = unsafe { &mut (*current).next as *mut *mut Action };
372            }
373            Err(IrqError::NotFound)
374        })();
375        self.lock.unlock(&self.ops, irq_state);
376        result
377    }
378
379    fn drop_detached_action(&self, handle: IrqHandle) {
380        if let Ok((action, _scope)) = self.detach_action(handle)
381            && self.wait_and_remove_action(handle.irq, action).is_ok()
382        {
383            unsafe {
384                drop(Box::from_raw(action));
385            }
386        }
387    }
388
389    fn with_action<T>(
390        &self,
391        handle: IrqHandle,
392        f: impl FnOnce(&Action) -> T,
393    ) -> Result<T, IrqError> {
394        let irq_state = self.lock.lock(&self.ops);
395        let result = (|| {
396            let action = self.find_action(handle).ok_or(IrqError::NotFound)?;
397            Ok(f(action))
398        })();
399        self.lock.unlock(&self.ops, irq_state);
400        result
401    }
402
403    fn apply_enabled(
404        &self,
405        handle: IrqHandle,
406        scope: IrqScope,
407        enabled: bool,
408    ) -> Result<(), IrqError> {
409        match scope {
410            IrqScope::Global => self.apply_line_state(handle.irq, None),
411            IrqScope::PerCpu { cpus } => {
412                for cpu in cpus.iter() {
413                    self.apply_percpu_enabled(handle, cpu, enabled)?;
414                }
415                Ok(())
416            }
417        }
418    }
419
420    fn apply_affinity(&self, irq: IrqId, affinity: IrqAffinity) -> Result<(), IrqError> {
421        match affinity {
422            IrqAffinity::Any => Ok(()),
423            IrqAffinity::Fixed(cpu) if self.ops.cpu_online(cpu) => {
424                self.ops.set_affinity(irq, affinity)
425            }
426            IrqAffinity::Fixed(_) => Err(IrqError::CpuOffline),
427        }
428    }
429
430    fn apply_percpu_enabled(
431        &self,
432        handle: IrqHandle,
433        cpu: CpuId,
434        enabled: bool,
435    ) -> Result<(), IrqError> {
436        if self.ops.cpu_online(cpu) {
437            self.apply_line_state(handle.irq, Some(cpu))?;
438        } else if enabled {
439            self.with_action(handle, |action| {
440                action.insert_pending_enable(cpu);
441            })?;
442        } else {
443            self.with_action(handle, |action| {
444                action.remove_pending_enable(cpu);
445            })?;
446        }
447        Ok(())
448    }
449
450    fn apply_scope_line_state(&self, irq: IrqId, scope: IrqScope) -> Result<(), IrqError> {
451        match scope {
452            IrqScope::Global => self.apply_line_state(irq, None),
453            IrqScope::PerCpu { cpus } => {
454                for cpu in cpus.iter() {
455                    self.apply_line_state(irq, Some(cpu))?;
456                }
457                Ok(())
458            }
459        }
460    }
461
462    fn snapshot_and_disable_scope_line(
463        &self,
464        irq: IrqId,
465        scope: IrqScope,
466    ) -> Result<LineStateSnapshot, IrqError> {
467        let mut snapshot = LineStateSnapshot::new(scope);
468        match scope {
469            IrqScope::Global => {
470                snapshot.global = self.snapshot_and_disable_line(irq, None)?;
471            }
472            IrqScope::PerCpu { cpus } => {
473                for cpu in cpus.iter() {
474                    if !self.ops.cpu_online(cpu) {
475                        continue;
476                    }
477                    match self.snapshot_and_disable_line(irq, Some(cpu)) {
478                        Ok(was_enabled) => snapshot.percpu.push((cpu, was_enabled)),
479                        Err(err) => {
480                            let _ = self.restore_scope_line_snapshot(irq, scope, &snapshot);
481                            return Err(err);
482                        }
483                    }
484                }
485            }
486        }
487        Ok(snapshot)
488    }
489
490    fn snapshot_and_disable_line(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<bool, IrqError> {
491        let was_enabled = self.controller_line_enabled(irq, cpu)?;
492        self.set_controller_enabled(irq, cpu, false)?;
493        self.set_line_applied_if_present(irq, cpu, false)?;
494        Ok(was_enabled)
495    }
496
497    fn restore_scope_line_snapshot(
498        &self,
499        irq: IrqId,
500        scope: IrqScope,
501        snapshot: &LineStateSnapshot,
502    ) -> Result<(), IrqError> {
503        match scope {
504            IrqScope::Global => {
505                self.restore_line_snapshot(irq, None, snapshot.global)?;
506            }
507            IrqScope::PerCpu { cpus } => {
508                for cpu in cpus.iter() {
509                    if let Some((_, was_enabled)) = snapshot
510                        .percpu
511                        .iter()
512                        .find(|(snapshot_cpu, _)| *snapshot_cpu == cpu)
513                    {
514                        self.restore_line_snapshot(irq, Some(cpu), *was_enabled)?;
515                    }
516                }
517            }
518        }
519        Ok(())
520    }
521
522    fn restore_line_snapshot(
523        &self,
524        irq: IrqId,
525        cpu: Option<CpuId>,
526        was_enabled: bool,
527    ) -> Result<(), IrqError> {
528        if was_enabled {
529            self.set_controller_enabled(irq, cpu, true)?;
530        }
531        self.set_line_applied_if_present(irq, cpu, was_enabled)?;
532        Ok(())
533    }
534
535    fn controller_line_enabled(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<bool, IrqError> {
536        match self.ops.is_enabled(irq, cpu) {
537            Ok(enabled) => Ok(enabled),
538            Err(IrqError::Unsupported) => {
539                Ok(self.framework_line_enabled(irq, cpu).unwrap_or(false))
540            }
541            Err(err) => Err(err),
542        }
543    }
544
545    fn apply_line_state(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<(), IrqError> {
546        loop {
547            if let Some(cpu) = cpu
548                && !self.ops.cpu_online(cpu)
549            {
550                return Ok(());
551            }
552
553            let Some((desired, applied)) = self.line_state(irq, cpu) else {
554                return Err(IrqError::NotFound);
555            };
556            if desired == applied {
557                return Ok(());
558            }
559
560            self.set_controller_enabled(irq, cpu, desired)?;
561            self.set_line_applied(irq, cpu, desired)?;
562        }
563    }
564
565    fn set_controller_enabled(
566        &self,
567        irq: IrqId,
568        cpu: Option<CpuId>,
569        enabled: bool,
570    ) -> Result<(), IrqError> {
571        match cpu {
572            None => self.ops.set_enabled(irq, None, enabled),
573            Some(cpu) if cpu == self.ops.current_cpu() => {
574                self.ops.set_enabled(irq, Some(cpu), enabled)
575            }
576            Some(cpu) => {
577                if self.ops.in_irq_context() {
578                    return Err(IrqError::InIrqContext);
579                }
580                let mut request = RemoteEnable {
581                    registry: self as *const Self as *mut (),
582                    irq,
583                    cpu,
584                    enabled,
585                    result: Ok(()),
586                };
587                self.ops.run_on_cpu_sync(
588                    cpu,
589                    remote_enable_thunk::<O>,
590                    (&mut request as *mut RemoteEnable).cast(),
591                )?;
592                request.result
593            }
594        }
595    }
596
597    fn begin_dispatch(&self, irq: IrqId) -> Option<*mut Action> {
598        let irq_state = self.lock.lock(&self.ops);
599        let result = {
600            let state = unsafe { &mut *self.state.get() };
601            state
602                .descriptors
603                .iter_mut()
604                .find(|descriptor| descriptor.irq == irq)
605                .and_then(|descriptor| {
606                    if descriptor.head.is_null() {
607                        None
608                    } else {
609                        descriptor.in_flight.fetch_add(1, Ordering::AcqRel);
610                        Some(descriptor.head)
611                    }
612                })
613        };
614        self.lock.unlock(&self.ops, irq_state);
615        result
616    }
617
618    fn end_dispatch(&self, irq: IrqId) {
619        let irq_state = self.lock.lock(&self.ops);
620        let state = unsafe { &mut *self.state.get() };
621        if let Some(descriptor) = state
622            .descriptors
623            .iter_mut()
624            .find(|descriptor| descriptor.irq == irq)
625        {
626            descriptor.in_flight.fetch_sub(1, Ordering::AcqRel);
627        }
628        self.lock.unlock(&self.ops, irq_state);
629    }
630
631    fn pending_enables_for_cpu(&self, cpu: CpuId) -> Vec<IrqId> {
632        let irq_state = self.lock.lock(&self.ops);
633        let mut pending = Vec::new();
634        for descriptor in &self.state_ref().descriptors {
635            if descriptor.actions().any(|action| {
636                let action = unsafe { &*action };
637                !action.detached.load(Ordering::Acquire)
638                    && action.pending_enable_contains(cpu)
639                    && action_matches_cpu(action.scope, cpu)
640            }) {
641                pending.push(descriptor.irq);
642            }
643        }
644        self.lock.unlock(&self.ops, irq_state);
645        pending
646    }
647
648    fn clear_pending_enable_for_cpu(&self, irq: IrqId, cpu: CpuId) {
649        let irq_state = self.lock.lock(&self.ops);
650        if let Some(descriptor) = self.descriptor(irq) {
651            for action in descriptor.actions() {
652                let action = unsafe { &*action };
653                if action_matches_cpu(action.scope, cpu) {
654                    action.remove_pending_enable(cpu);
655                }
656            }
657        }
658        self.lock.unlock(&self.ops, irq_state);
659    }
660
661    fn line_state(&self, irq: IrqId, cpu: Option<CpuId>) -> Option<(bool, bool)> {
662        let irq_state = self.lock.lock(&self.ops);
663        let result = self
664            .descriptor(irq)
665            .map(|descriptor| (descriptor.line_desired(cpu), descriptor.line_applied(cpu)));
666        self.lock.unlock(&self.ops, irq_state);
667        result
668    }
669
670    fn set_line_applied(
671        &self,
672        irq: IrqId,
673        cpu: Option<CpuId>,
674        enabled: bool,
675    ) -> Result<(), IrqError> {
676        let irq_state = self.lock.lock(&self.ops);
677        let result = (|| {
678            let state = unsafe { &mut *self.state.get() };
679            let descriptor = state
680                .descriptors
681                .iter_mut()
682                .find(|descriptor| descriptor.irq == irq)
683                .ok_or(IrqError::NotFound)?;
684            descriptor.set_line_applied(cpu, enabled);
685            Ok(())
686        })();
687        self.lock.unlock(&self.ops, irq_state);
688        result
689    }
690
691    fn set_line_applied_if_present(
692        &self,
693        irq: IrqId,
694        cpu: Option<CpuId>,
695        enabled: bool,
696    ) -> Result<(), IrqError> {
697        let irq_state = self.lock.lock(&self.ops);
698        let result = {
699            let state = unsafe { &mut *self.state.get() };
700            if let Some(descriptor) = state
701                .descriptors
702                .iter_mut()
703                .find(|descriptor| descriptor.irq == irq)
704            {
705                descriptor.set_line_applied(cpu, enabled);
706            }
707            Ok(())
708        };
709        self.lock.unlock(&self.ops, irq_state);
710        result
711    }
712
713    fn framework_line_enabled(&self, irq: IrqId, cpu: Option<CpuId>) -> Result<bool, IrqError> {
714        let irq_state = self.lock.lock(&self.ops);
715        let result = (|| {
716            let descriptor = self.descriptor(irq).ok_or(IrqError::NotFound)?;
717            Ok(descriptor.line_applied(cpu))
718        })();
719        self.lock.unlock(&self.ops, irq_state);
720        result
721    }
722
723    fn find_action(&self, handle: IrqHandle) -> Option<&Action> {
724        self.descriptor(handle.irq)?
725            .actions()
726            .map(|action| unsafe { &*action })
727            .find(|action| action.id == handle.id && !action.detached.load(Ordering::Acquire))
728    }
729
730    fn descriptor(&self, irq: IrqId) -> Option<&Descriptor> {
731        self.state_ref()
732            .descriptors
733            .iter()
734            .find(|descriptor| descriptor.irq == irq)
735    }
736
737    fn state_ref(&self) -> &RegistryState {
738        unsafe { &*self.state.get() }
739    }
740}
741
742struct LineStateSnapshot {
743    global: bool,
744    percpu: Vec<(CpuId, bool)>,
745}
746
747impl LineStateSnapshot {
748    fn new(scope: IrqScope) -> Self {
749        Self {
750            global: false,
751            percpu: match scope {
752                IrqScope::Global => Vec::new(),
753                IrqScope::PerCpu { cpus } => Vec::with_capacity(cpus.iter().count()),
754            },
755        }
756    }
757}
758
759struct DispatchGuard<'a, O: IrqOps> {
760    registry: &'a Registry<O>,
761    irq: IrqId,
762}
763
764struct ActionRunGuard<'a> {
765    action: &'a Action,
766}
767
768impl<'a> ActionRunGuard<'a> {
769    fn enter(action: &'a Action) -> Option<Self> {
770        match action.execution {
771            IrqExecution::Concurrent => Some(Self { action }),
772            IrqExecution::NonReentrant => action
773                .running
774                .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
775                .ok()
776                .map(|_| Self { action }),
777        }
778    }
779}
780
781impl Drop for ActionRunGuard<'_> {
782    fn drop(&mut self) {
783        if self.action.execution == IrqExecution::NonReentrant {
784            self.action.running.store(false, Ordering::Release);
785        }
786    }
787}
788
789impl<O: IrqOps> Drop for DispatchGuard<'_, O> {
790    fn drop(&mut self) {
791        self.registry.end_dispatch(self.irq);
792    }
793}
794
795struct RemoteEnable {
796    registry: *mut (),
797    irq: IrqId,
798    cpu: CpuId,
799    enabled: bool,
800    result: Result<(), IrqError>,
801}
802
803unsafe fn remote_enable_thunk<O: IrqOps>(arg: *mut ()) {
804    let request = unsafe { &mut *arg.cast::<RemoteEnable>() };
805    let registry = unsafe { &*(request.registry as *const Registry<O>) };
806    request.result = registry
807        .ops
808        .set_enabled(request.irq, Some(request.cpu), request.enabled);
809}
810
811fn status_cpu(scope: IrqScope, current: CpuId) -> Option<CpuId> {
812    match scope {
813        IrqScope::Global => None,
814        IrqScope::PerCpu { .. } => Some(current),
815    }
816}