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, IrqNumber, IrqOps,
10    IrqOutcome, IrqRequest, IrqReturn, IrqScope, IrqStatus,
11    action::{Action, ActionHandler},
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: IrqNumber, 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: IrqNumber, 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.is_boxed() && request.execution == IrqExecution::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: IrqNumber,
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: IrqNumber, 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: IrqNumber, 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: IrqNumber, 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: IrqNumber, 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: IrqNumber,
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(
491        &self,
492        irq: IrqNumber,
493        cpu: Option<CpuId>,
494    ) -> Result<bool, IrqError> {
495        let was_enabled = self.controller_line_enabled(irq, cpu)?;
496        self.set_controller_enabled(irq, cpu, false)?;
497        self.set_line_applied_if_present(irq, cpu, false)?;
498        Ok(was_enabled)
499    }
500
501    fn restore_scope_line_snapshot(
502        &self,
503        irq: IrqNumber,
504        scope: IrqScope,
505        snapshot: &LineStateSnapshot,
506    ) -> Result<(), IrqError> {
507        match scope {
508            IrqScope::Global => {
509                self.restore_line_snapshot(irq, None, snapshot.global)?;
510            }
511            IrqScope::PerCpu { cpus } => {
512                for cpu in cpus.iter() {
513                    if let Some((_, was_enabled)) = snapshot
514                        .percpu
515                        .iter()
516                        .find(|(snapshot_cpu, _)| *snapshot_cpu == cpu)
517                    {
518                        self.restore_line_snapshot(irq, Some(cpu), *was_enabled)?;
519                    }
520                }
521            }
522        }
523        Ok(())
524    }
525
526    fn restore_line_snapshot(
527        &self,
528        irq: IrqNumber,
529        cpu: Option<CpuId>,
530        was_enabled: bool,
531    ) -> Result<(), IrqError> {
532        if was_enabled {
533            self.set_controller_enabled(irq, cpu, true)?;
534        }
535        self.set_line_applied_if_present(irq, cpu, was_enabled)?;
536        Ok(())
537    }
538
539    fn controller_line_enabled(
540        &self,
541        irq: IrqNumber,
542        cpu: Option<CpuId>,
543    ) -> Result<bool, IrqError> {
544        match self.ops.is_enabled(irq, cpu) {
545            Ok(enabled) => Ok(enabled),
546            Err(IrqError::Unsupported) => {
547                Ok(self.framework_line_enabled(irq, cpu).unwrap_or(false))
548            }
549            Err(err) => Err(err),
550        }
551    }
552
553    fn apply_line_state(&self, irq: IrqNumber, cpu: Option<CpuId>) -> Result<(), IrqError> {
554        loop {
555            if let Some(cpu) = cpu
556                && !self.ops.cpu_online(cpu)
557            {
558                return Ok(());
559            }
560
561            let Some((desired, applied)) = self.line_state(irq, cpu) else {
562                return Err(IrqError::NotFound);
563            };
564            if desired == applied {
565                return Ok(());
566            }
567
568            self.set_controller_enabled(irq, cpu, desired)?;
569            self.set_line_applied(irq, cpu, desired)?;
570        }
571    }
572
573    fn set_controller_enabled(
574        &self,
575        irq: IrqNumber,
576        cpu: Option<CpuId>,
577        enabled: bool,
578    ) -> Result<(), IrqError> {
579        match cpu {
580            None => self.ops.set_enabled(irq, None, enabled),
581            Some(cpu) if cpu == self.ops.current_cpu() => {
582                self.ops.set_enabled(irq, Some(cpu), enabled)
583            }
584            Some(cpu) => {
585                let mut request = RemoteEnable {
586                    registry: self as *const Self as *mut (),
587                    irq,
588                    cpu,
589                    enabled,
590                    result: Ok(()),
591                };
592                self.ops.run_on_cpu_sync(
593                    cpu,
594                    remote_enable_thunk::<O>,
595                    (&mut request as *mut RemoteEnable).cast(),
596                )?;
597                request.result
598            }
599        }
600    }
601
602    fn begin_dispatch(&self, irq: IrqNumber) -> Option<*mut Action> {
603        let irq_state = self.lock.lock(&self.ops);
604        let result = {
605            let state = unsafe { &mut *self.state.get() };
606            state
607                .descriptors
608                .iter_mut()
609                .find(|descriptor| descriptor.irq == irq)
610                .and_then(|descriptor| {
611                    if descriptor.head.is_null() {
612                        None
613                    } else {
614                        descriptor.in_flight.fetch_add(1, Ordering::AcqRel);
615                        Some(descriptor.head)
616                    }
617                })
618        };
619        self.lock.unlock(&self.ops, irq_state);
620        result
621    }
622
623    fn end_dispatch(&self, irq: IrqNumber) {
624        let irq_state = self.lock.lock(&self.ops);
625        let state = unsafe { &mut *self.state.get() };
626        if let Some(descriptor) = state
627            .descriptors
628            .iter_mut()
629            .find(|descriptor| descriptor.irq == irq)
630        {
631            descriptor.in_flight.fetch_sub(1, Ordering::AcqRel);
632        }
633        self.lock.unlock(&self.ops, irq_state);
634    }
635
636    fn pending_enables_for_cpu(&self, cpu: CpuId) -> Vec<IrqNumber> {
637        let irq_state = self.lock.lock(&self.ops);
638        let mut pending = Vec::new();
639        for descriptor in &self.state_ref().descriptors {
640            if descriptor.actions().any(|action| {
641                let action = unsafe { &*action };
642                !action.detached.load(Ordering::Acquire)
643                    && action.pending_enable_contains(cpu)
644                    && action_matches_cpu(action.scope, cpu)
645            }) {
646                pending.push(descriptor.irq);
647            }
648        }
649        self.lock.unlock(&self.ops, irq_state);
650        pending
651    }
652
653    fn clear_pending_enable_for_cpu(&self, irq: IrqNumber, cpu: CpuId) {
654        let irq_state = self.lock.lock(&self.ops);
655        if let Some(descriptor) = self.descriptor(irq) {
656            for action in descriptor.actions() {
657                let action = unsafe { &*action };
658                if action_matches_cpu(action.scope, cpu) {
659                    action.remove_pending_enable(cpu);
660                }
661            }
662        }
663        self.lock.unlock(&self.ops, irq_state);
664    }
665
666    fn line_state(&self, irq: IrqNumber, cpu: Option<CpuId>) -> Option<(bool, bool)> {
667        let irq_state = self.lock.lock(&self.ops);
668        let result = self
669            .descriptor(irq)
670            .map(|descriptor| (descriptor.line_desired(cpu), descriptor.line_applied(cpu)));
671        self.lock.unlock(&self.ops, irq_state);
672        result
673    }
674
675    fn set_line_applied(
676        &self,
677        irq: IrqNumber,
678        cpu: Option<CpuId>,
679        enabled: bool,
680    ) -> Result<(), IrqError> {
681        let irq_state = self.lock.lock(&self.ops);
682        let result = (|| {
683            let state = unsafe { &mut *self.state.get() };
684            let descriptor = state
685                .descriptors
686                .iter_mut()
687                .find(|descriptor| descriptor.irq == irq)
688                .ok_or(IrqError::NotFound)?;
689            descriptor.set_line_applied(cpu, enabled);
690            Ok(())
691        })();
692        self.lock.unlock(&self.ops, irq_state);
693        result
694    }
695
696    fn set_line_applied_if_present(
697        &self,
698        irq: IrqNumber,
699        cpu: Option<CpuId>,
700        enabled: bool,
701    ) -> Result<(), IrqError> {
702        let irq_state = self.lock.lock(&self.ops);
703        let result = {
704            let state = unsafe { &mut *self.state.get() };
705            if let Some(descriptor) = state
706                .descriptors
707                .iter_mut()
708                .find(|descriptor| descriptor.irq == irq)
709            {
710                descriptor.set_line_applied(cpu, enabled);
711            }
712            Ok(())
713        };
714        self.lock.unlock(&self.ops, irq_state);
715        result
716    }
717
718    fn framework_line_enabled(&self, irq: IrqNumber, cpu: Option<CpuId>) -> Result<bool, IrqError> {
719        let irq_state = self.lock.lock(&self.ops);
720        let result = (|| {
721            let descriptor = self.descriptor(irq).ok_or(IrqError::NotFound)?;
722            Ok(descriptor.line_applied(cpu))
723        })();
724        self.lock.unlock(&self.ops, irq_state);
725        result
726    }
727
728    fn find_action(&self, handle: IrqHandle) -> Option<&Action> {
729        self.descriptor(handle.irq)?
730            .actions()
731            .map(|action| unsafe { &*action })
732            .find(|action| action.id == handle.id && !action.detached.load(Ordering::Acquire))
733    }
734
735    fn descriptor(&self, irq: IrqNumber) -> Option<&Descriptor> {
736        self.state_ref()
737            .descriptors
738            .iter()
739            .find(|descriptor| descriptor.irq == irq)
740    }
741
742    fn state_ref(&self) -> &RegistryState {
743        unsafe { &*self.state.get() }
744    }
745}
746
747impl Action {
748    fn call(&self, ctx: IrqContext) -> IrqReturn {
749        match &self.handler {
750            ActionHandler::Raw { handler, data } => unsafe { handler(ctx, *data) },
751            ActionHandler::Boxed(handler) => {
752                let handler = unsafe { &mut *handler.get() };
753                handler(ctx)
754            }
755        }
756    }
757}
758
759struct LineStateSnapshot {
760    global: bool,
761    percpu: Vec<(CpuId, bool)>,
762}
763
764impl LineStateSnapshot {
765    fn new(scope: IrqScope) -> Self {
766        Self {
767            global: false,
768            percpu: match scope {
769                IrqScope::Global => Vec::new(),
770                IrqScope::PerCpu { cpus } => Vec::with_capacity(cpus.iter().count()),
771            },
772        }
773    }
774}
775
776struct DispatchGuard<'a, O: IrqOps> {
777    registry: &'a Registry<O>,
778    irq: IrqNumber,
779}
780
781struct ActionRunGuard<'a> {
782    action: &'a Action,
783}
784
785impl<'a> ActionRunGuard<'a> {
786    fn enter(action: &'a Action) -> Option<Self> {
787        match action.execution {
788            IrqExecution::Concurrent => Some(Self { action }),
789            IrqExecution::NonReentrant => action
790                .running
791                .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
792                .ok()
793                .map(|_| Self { action }),
794        }
795    }
796}
797
798impl Drop for ActionRunGuard<'_> {
799    fn drop(&mut self) {
800        if self.action.execution == IrqExecution::NonReentrant {
801            self.action.running.store(false, Ordering::Release);
802        }
803    }
804}
805
806impl<O: IrqOps> Drop for DispatchGuard<'_, O> {
807    fn drop(&mut self) {
808        self.registry.end_dispatch(self.irq);
809    }
810}
811
812struct RemoteEnable {
813    registry: *mut (),
814    irq: IrqNumber,
815    cpu: CpuId,
816    enabled: bool,
817    result: Result<(), IrqError>,
818}
819
820unsafe fn remote_enable_thunk<O: IrqOps>(arg: *mut ()) {
821    let request = unsafe { &mut *arg.cast::<RemoteEnable>() };
822    let registry = unsafe { &*(request.registry as *const Registry<O>) };
823    request.result = registry
824        .ops
825        .set_enabled(request.irq, Some(request.cpu), request.enabled);
826}
827
828fn status_cpu(scope: IrqScope, current: CpuId) -> Option<CpuId> {
829    match scope {
830        IrqScope::Global => None,
831        IrqScope::PerCpu { .. } => Some(current),
832    }
833}