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