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    AutoEnable, CpuId, IrqContext, IrqError, IrqHandle, IrqNumber, IrqOps, IrqOutcome, IrqRequest,
10    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: IrqNumber, request: IrqRequest) -> Result<IrqHandle, IrqError> {
52        self.validate_request(&request)?;
53
54        let id = self.next_id.fetch_add(1, Ordering::Relaxed);
55        let action = Box::new(Action::new(id, &request));
56        let action = Box::into_raw(action);
57        let irq_state = self.lock.lock(&self.ops);
58        let result = self.insert_action_locked(irq, &request, action);
59        self.lock.unlock(&self.ops, irq_state);
60
61        if let Err(err) = result {
62            unsafe {
63                drop(Box::from_raw(action));
64            }
65            return Err(err);
66        }
67
68        let handle = IrqHandle { irq, id };
69        if request.auto_enable == AutoEnable::Yes
70            && let Err(err) = self.enable(handle)
71        {
72            self.drop_detached_action(handle);
73            return Err(err);
74        }
75        Ok(handle)
76    }
77
78    /// Frees an IRQ action.
79    pub fn free(&self, handle: IrqHandle) -> Result<(), IrqError> {
80        if self.ops.in_irq_context() {
81            return Err(IrqError::InIrqContext);
82        }
83        let (action, scope) = self.detach_action(handle)?;
84        let mut result = self.apply_scope_line_state(handle.irq, scope);
85        if let Err(err) = self.wait_and_remove_action(handle.irq, action)
86            && result.is_ok()
87        {
88            result = Err(err);
89        }
90        unsafe {
91            drop(Box::from_raw(action));
92        }
93        result
94    }
95
96    /// Enables an IRQ action and its backing line.
97    pub fn enable(&self, handle: IrqHandle) -> Result<(), IrqError> {
98        let scope = self.set_action_enabled(handle, true)?;
99
100        if let Err(err) = self.apply_enabled(handle, scope, true) {
101            let _ = self.disable(handle);
102            return Err(err);
103        }
104        Ok(())
105    }
106
107    /// Disables an IRQ action and its backing line.
108    pub fn disable(&self, handle: IrqHandle) -> Result<(), IrqError> {
109        let scope = self.set_action_enabled(handle, false)?;
110        self.apply_enabled(handle, scope, false)
111    }
112
113    fn set_action_enabled(&self, handle: IrqHandle, enabled: bool) -> Result<IrqScope, IrqError> {
114        let irq_state = self.lock.lock(&self.ops);
115        let result = (|| {
116            let state = unsafe { &mut *self.state.get() };
117            let descriptor = state
118                .descriptors
119                .iter_mut()
120                .find(|descriptor| descriptor.irq == handle.irq)
121                .ok_or(IrqError::NotFound)?;
122            let action = descriptor
123                .actions()
124                .find(|action| unsafe { (**action).id == handle.id })
125                .ok_or(IrqError::NotFound)?;
126            unsafe {
127                if (*action).detached.load(Ordering::Acquire) {
128                    return Err(IrqError::NotFound);
129                }
130                (*action).enabled.store(enabled, Ordering::Release);
131                (*action).clear_pending_enable_all();
132                let scope = (*action).scope;
133                recompute_scope_line_desired(descriptor, scope);
134                Ok(scope)
135            }
136        })();
137        self.lock.unlock(&self.ops, irq_state);
138        result
139    }
140
141    /// Returns a status snapshot for an IRQ action.
142    pub fn status(&self, handle: IrqHandle) -> Result<IrqStatus, IrqError> {
143        let (scope, action_enabled, in_flight) = self.with_action(handle, |action| {
144            let in_flight = self
145                .descriptor(handle.irq)
146                .map(|desc| desc.in_flight.load(Ordering::Acquire))
147                .unwrap_or(0);
148            (
149                action.scope,
150                action.enabled.load(Ordering::Acquire),
151                in_flight,
152            )
153        })?;
154        let cpu = status_cpu(scope, self.ops.current_cpu());
155        let line_enabled = match self.ops.is_enabled(handle.irq, cpu) {
156            Ok(enabled) => enabled,
157            Err(IrqError::Unsupported) => self.framework_line_enabled(handle.irq, cpu)?,
158            Err(err) => return Err(err),
159        };
160        let pending = match self.ops.is_pending(handle.irq, cpu) {
161            Ok(pending) => pending,
162            Err(IrqError::Unsupported) => false,
163            Err(err) => return Err(err),
164        };
165        let in_service = match self.ops.is_in_service(handle.irq, cpu) {
166            Ok(in_service) => in_service,
167            Err(IrqError::Unsupported) => false,
168            Err(err) => return Err(err),
169        };
170        Ok(IrqStatus {
171            action_enabled,
172            line_enabled,
173            pending,
174            in_service,
175            in_flight,
176        })
177    }
178
179    /// Dispatches an IRQ on the given CPU.
180    pub fn dispatch(&self, irq: IrqNumber, cpu: CpuId) -> IrqOutcome {
181        let Some(head) = self.begin_dispatch(irq) else {
182            return IrqOutcome::default();
183        };
184        let _guard = DispatchGuard {
185            registry: self,
186            irq,
187        };
188
189        let mut outcome = IrqOutcome::default();
190        let ctx = IrqContext { irq, cpu };
191        let mut next = head;
192        while !next.is_null() {
193            let action = unsafe { &*next };
194            next = action.next;
195            if action.detached.load(Ordering::Acquire)
196                || !action.enabled.load(Ordering::Acquire)
197                || !action_matches_cpu(action.scope, cpu)
198            {
199                continue;
200            }
201
202            outcome.called += 1;
203            match unsafe { (action.handler)(ctx, action.data) } {
204                IrqReturn::Unhandled => {}
205                IrqReturn::Handled => outcome.handled = true,
206                IrqReturn::Wake => {
207                    outcome.handled = true;
208                    outcome.wake = true;
209                }
210            }
211        }
212
213        outcome
214    }
215
216    /// Marks a CPU online and applies pending per-CPU enables for that CPU.
217    pub fn cpu_online(&self, cpu: CpuId) -> Result<(), IrqError> {
218        if !self.ops.cpu_online(cpu) {
219            return Err(IrqError::CpuOffline);
220        }
221        let pending = self.pending_enables_for_cpu(cpu);
222        for irq in pending {
223            self.apply_line_state(irq, Some(cpu))?;
224            self.clear_pending_enable_for_cpu(irq, cpu);
225        }
226        Ok(())
227    }
228
229    /// Marks a CPU offline from the framework's perspective.
230    pub fn cpu_offline(&self, cpu: CpuId) -> Result<(), IrqError> {
231        if self.ops.cpu_online(cpu) {
232            return Err(IrqError::Unsupported);
233        }
234        Ok(())
235    }
236
237    fn validate_request(&self, request: &IrqRequest) -> Result<(), IrqError> {
238        if let IrqScope::PerCpu { cpus } = request.scope
239            && cpus.is_empty()
240        {
241            return Err(IrqError::InvalidCpu);
242        }
243        Ok(())
244    }
245
246    fn insert_action_locked(
247        &self,
248        irq: IrqNumber,
249        request: &IrqRequest,
250        action: *mut Action,
251    ) -> Result<(), IrqError> {
252        let state = unsafe { &mut *self.state.get() };
253        let descriptor = match state
254            .descriptors
255            .iter_mut()
256            .find(|descriptor| descriptor.irq == irq)
257        {
258            Some(descriptor) => descriptor,
259            None => {
260                state.descriptors.push(Descriptor::new(irq, request));
261                state.descriptors.last_mut().ok_or(IrqError::NoMemory)?
262            }
263        };
264        descriptor.compatible_with(request)?;
265        unsafe {
266            (*action).next = descriptor.head;
267        }
268        descriptor.head = action;
269        Ok(())
270    }
271
272    fn detach_action(&self, handle: IrqHandle) -> Result<(*mut Action, IrqScope), IrqError> {
273        let irq_state = self.lock.lock(&self.ops);
274        let result = (|| {
275            let state = unsafe { &mut *self.state.get() };
276            let descriptor = state
277                .descriptors
278                .iter_mut()
279                .find(|descriptor| descriptor.irq == handle.irq)
280                .ok_or(IrqError::NotFound)?;
281            let action = descriptor
282                .actions()
283                .find(|action| unsafe { (**action).id == handle.id })
284                .ok_or(IrqError::NotFound)?;
285            unsafe {
286                if (*action).detached.swap(true, Ordering::AcqRel) {
287                    return Err(IrqError::NotFound);
288                }
289                (*action).enabled.store(false, Ordering::Release);
290                (*action).clear_pending_enable_all();
291                let scope = (*action).scope;
292                recompute_scope_line_desired(descriptor, scope);
293                Ok((action, scope))
294            }
295        })();
296        self.lock.unlock(&self.ops, irq_state);
297        result
298    }
299
300    fn wait_and_remove_action(&self, irq: IrqNumber, action: *mut Action) -> Result<(), IrqError> {
301        loop {
302            match self.try_remove_action(irq, action) {
303                Err(IrqError::Busy) => self.ops.relax(),
304                result => return result,
305            }
306        }
307    }
308
309    fn try_remove_action(&self, irq: IrqNumber, action: *mut Action) -> Result<(), IrqError> {
310        let irq_state = self.lock.lock(&self.ops);
311        let result = (|| {
312            let state = unsafe { &mut *self.state.get() };
313            let descriptor = state
314                .descriptors
315                .iter_mut()
316                .find(|descriptor| descriptor.irq == irq)
317                .ok_or(IrqError::NotFound)?;
318            if descriptor.in_flight.load(Ordering::Acquire) != 0 {
319                return Err(IrqError::Busy);
320            }
321            let mut link = &mut descriptor.head as *mut *mut Action;
322            while unsafe { !(*link).is_null() } {
323                let current = unsafe { *link };
324                if current == action {
325                    unsafe {
326                        *link = (*current).next;
327                        (*current).next = ptr::null_mut();
328                    }
329                    return Ok(());
330                }
331                link = unsafe { &mut (*current).next as *mut *mut Action };
332            }
333            Err(IrqError::NotFound)
334        })();
335        self.lock.unlock(&self.ops, irq_state);
336        result
337    }
338
339    fn drop_detached_action(&self, handle: IrqHandle) {
340        if let Ok((action, _scope)) = self.detach_action(handle)
341            && self.wait_and_remove_action(handle.irq, action).is_ok()
342        {
343            unsafe {
344                drop(Box::from_raw(action));
345            }
346        }
347    }
348
349    fn with_action<T>(
350        &self,
351        handle: IrqHandle,
352        f: impl FnOnce(&Action) -> T,
353    ) -> Result<T, IrqError> {
354        let irq_state = self.lock.lock(&self.ops);
355        let result = (|| {
356            let action = self.find_action(handle).ok_or(IrqError::NotFound)?;
357            Ok(f(action))
358        })();
359        self.lock.unlock(&self.ops, irq_state);
360        result
361    }
362
363    fn apply_enabled(
364        &self,
365        handle: IrqHandle,
366        scope: IrqScope,
367        enabled: bool,
368    ) -> Result<(), IrqError> {
369        match scope {
370            IrqScope::Global => self.apply_line_state(handle.irq, None),
371            IrqScope::PerCpu { cpus } => {
372                for cpu in cpus.iter() {
373                    self.apply_percpu_enabled(handle, cpu, enabled)?;
374                }
375                Ok(())
376            }
377        }
378    }
379
380    fn apply_percpu_enabled(
381        &self,
382        handle: IrqHandle,
383        cpu: CpuId,
384        enabled: bool,
385    ) -> Result<(), IrqError> {
386        if self.ops.cpu_online(cpu) {
387            self.apply_line_state(handle.irq, Some(cpu))?;
388        } else if enabled {
389            self.with_action(handle, |action| {
390                action.insert_pending_enable(cpu);
391            })?;
392        } else {
393            self.with_action(handle, |action| {
394                action.remove_pending_enable(cpu);
395            })?;
396        }
397        Ok(())
398    }
399
400    fn apply_scope_line_state(&self, irq: IrqNumber, scope: IrqScope) -> Result<(), IrqError> {
401        match scope {
402            IrqScope::Global => self.apply_line_state(irq, None),
403            IrqScope::PerCpu { cpus } => {
404                for cpu in cpus.iter() {
405                    self.apply_line_state(irq, Some(cpu))?;
406                }
407                Ok(())
408            }
409        }
410    }
411
412    fn apply_line_state(&self, irq: IrqNumber, cpu: Option<CpuId>) -> Result<(), IrqError> {
413        loop {
414            if let Some(cpu) = cpu
415                && !self.ops.cpu_online(cpu)
416            {
417                return Ok(());
418            }
419
420            let Some((desired, applied)) = self.line_state(irq, cpu) else {
421                return Err(IrqError::NotFound);
422            };
423            if desired == applied {
424                return Ok(());
425            }
426
427            self.set_controller_enabled(irq, cpu, desired)?;
428            self.set_line_applied(irq, cpu, desired)?;
429        }
430    }
431
432    fn set_controller_enabled(
433        &self,
434        irq: IrqNumber,
435        cpu: Option<CpuId>,
436        enabled: bool,
437    ) -> Result<(), IrqError> {
438        match cpu {
439            None => self.ops.set_enabled(irq, None, enabled),
440            Some(cpu) if cpu == self.ops.current_cpu() => {
441                self.ops.set_enabled(irq, Some(cpu), enabled)
442            }
443            Some(cpu) => {
444                let mut request = RemoteEnable {
445                    registry: self as *const Self as *mut (),
446                    irq,
447                    cpu,
448                    enabled,
449                    result: Ok(()),
450                };
451                self.ops.run_on_cpu_sync(
452                    cpu,
453                    remote_enable_thunk::<O>,
454                    (&mut request as *mut RemoteEnable).cast(),
455                )?;
456                request.result
457            }
458        }
459    }
460
461    fn begin_dispatch(&self, irq: IrqNumber) -> Option<*mut Action> {
462        let irq_state = self.lock.lock(&self.ops);
463        let result = {
464            let state = unsafe { &mut *self.state.get() };
465            state
466                .descriptors
467                .iter_mut()
468                .find(|descriptor| descriptor.irq == irq)
469                .and_then(|descriptor| {
470                    if descriptor.head.is_null() {
471                        None
472                    } else {
473                        descriptor.in_flight.fetch_add(1, Ordering::AcqRel);
474                        Some(descriptor.head)
475                    }
476                })
477        };
478        self.lock.unlock(&self.ops, irq_state);
479        result
480    }
481
482    fn end_dispatch(&self, irq: IrqNumber) {
483        let irq_state = self.lock.lock(&self.ops);
484        let state = unsafe { &mut *self.state.get() };
485        if let Some(descriptor) = state
486            .descriptors
487            .iter_mut()
488            .find(|descriptor| descriptor.irq == irq)
489        {
490            descriptor.in_flight.fetch_sub(1, Ordering::AcqRel);
491        }
492        self.lock.unlock(&self.ops, irq_state);
493    }
494
495    fn pending_enables_for_cpu(&self, cpu: CpuId) -> Vec<IrqNumber> {
496        let irq_state = self.lock.lock(&self.ops);
497        let mut pending = Vec::new();
498        for descriptor in &self.state_ref().descriptors {
499            if descriptor.actions().any(|action| {
500                let action = unsafe { &*action };
501                !action.detached.load(Ordering::Acquire)
502                    && action.pending_enable_contains(cpu)
503                    && action_matches_cpu(action.scope, cpu)
504            }) {
505                pending.push(descriptor.irq);
506            }
507        }
508        self.lock.unlock(&self.ops, irq_state);
509        pending
510    }
511
512    fn clear_pending_enable_for_cpu(&self, irq: IrqNumber, cpu: CpuId) {
513        let irq_state = self.lock.lock(&self.ops);
514        if let Some(descriptor) = self.descriptor(irq) {
515            for action in descriptor.actions() {
516                let action = unsafe { &*action };
517                if action_matches_cpu(action.scope, cpu) {
518                    action.remove_pending_enable(cpu);
519                }
520            }
521        }
522        self.lock.unlock(&self.ops, irq_state);
523    }
524
525    fn line_state(&self, irq: IrqNumber, cpu: Option<CpuId>) -> Option<(bool, bool)> {
526        let irq_state = self.lock.lock(&self.ops);
527        let result = self
528            .descriptor(irq)
529            .map(|descriptor| (descriptor.line_desired(cpu), descriptor.line_applied(cpu)));
530        self.lock.unlock(&self.ops, irq_state);
531        result
532    }
533
534    fn set_line_applied(
535        &self,
536        irq: IrqNumber,
537        cpu: Option<CpuId>,
538        enabled: bool,
539    ) -> Result<(), IrqError> {
540        let irq_state = self.lock.lock(&self.ops);
541        let result = (|| {
542            let state = unsafe { &mut *self.state.get() };
543            let descriptor = state
544                .descriptors
545                .iter_mut()
546                .find(|descriptor| descriptor.irq == irq)
547                .ok_or(IrqError::NotFound)?;
548            descriptor.set_line_applied(cpu, enabled);
549            Ok(())
550        })();
551        self.lock.unlock(&self.ops, irq_state);
552        result
553    }
554
555    fn framework_line_enabled(&self, irq: IrqNumber, cpu: Option<CpuId>) -> Result<bool, IrqError> {
556        let irq_state = self.lock.lock(&self.ops);
557        let result = (|| {
558            let descriptor = self.descriptor(irq).ok_or(IrqError::NotFound)?;
559            Ok(descriptor.line_applied(cpu))
560        })();
561        self.lock.unlock(&self.ops, irq_state);
562        result
563    }
564
565    fn find_action(&self, handle: IrqHandle) -> Option<&Action> {
566        self.descriptor(handle.irq)?
567            .actions()
568            .map(|action| unsafe { &*action })
569            .find(|action| action.id == handle.id && !action.detached.load(Ordering::Acquire))
570    }
571
572    fn descriptor(&self, irq: IrqNumber) -> Option<&Descriptor> {
573        self.state_ref()
574            .descriptors
575            .iter()
576            .find(|descriptor| descriptor.irq == irq)
577    }
578
579    fn state_ref(&self) -> &RegistryState {
580        unsafe { &*self.state.get() }
581    }
582}
583
584struct DispatchGuard<'a, O: IrqOps> {
585    registry: &'a Registry<O>,
586    irq: IrqNumber,
587}
588
589impl<O: IrqOps> Drop for DispatchGuard<'_, O> {
590    fn drop(&mut self) {
591        self.registry.end_dispatch(self.irq);
592    }
593}
594
595struct RemoteEnable {
596    registry: *mut (),
597    irq: IrqNumber,
598    cpu: CpuId,
599    enabled: bool,
600    result: Result<(), IrqError>,
601}
602
603unsafe fn remote_enable_thunk<O: IrqOps>(arg: *mut ()) {
604    let request = unsafe { &mut *arg.cast::<RemoteEnable>() };
605    let registry = unsafe { &*(request.registry as *const Registry<O>) };
606    request.result = registry
607        .ops
608        .set_enabled(request.irq, Some(request.cpu), request.enabled);
609}
610
611fn status_cpu(scope: IrqScope, current: CpuId) -> Option<CpuId> {
612    match scope {
613        IrqScope::Global => None,
614        IrqScope::PerCpu { .. } => Some(current),
615    }
616}