ax_task/sync/bridge/
context.rs1use crate::sync::context::{ContextBackend, enter_preempt_irqsave, exit_preempt_irqsave};
2
3pub(super) const CONTEXT_RAW: u8 = 0;
4pub(super) const CONTEXT_PREEMPT: u8 = 1;
5pub(super) const CONTEXT_IRQSAVE: u8 = 2;
6pub(super) const CONTEXT_PREEMPT_IRQSAVE: u8 = 3;
7
8#[derive(Clone, Copy)]
10pub struct ContextOperations {
11 pub preempt_enter: fn() -> usize,
12 pub preempt_exit: unsafe fn(usize),
13 pub preempt_exit_irq_return: unsafe fn(usize),
14 pub irq_save_and_disable: fn() -> usize,
15 pub irq_restore: unsafe fn(usize),
16 pub hardirq_enter: fn(),
17 pub hardirq_exit: fn(),
18}
19
20struct ExternalContext<'ops>(&'ops ContextOperations);
21
22impl ContextBackend for ExternalContext<'_> {
23 type PreemptState = usize;
24 type IrqState = usize;
25
26 fn preempt_enter(&self) -> Self::PreemptState {
27 (self.0.preempt_enter)()
28 }
29
30 fn preempt_exit(&self, state: Self::PreemptState) {
31 unsafe { (self.0.preempt_exit)(state) };
33 }
34
35 fn preempt_exit_irq_return(&self, state: Self::PreemptState) {
36 unsafe { (self.0.preempt_exit_irq_return)(state) };
38 }
39
40 fn irq_save_and_disable(&self) -> Self::IrqState {
41 (self.0.irq_save_and_disable)()
42 }
43
44 fn irq_restore(&self, state: Self::IrqState) {
45 unsafe { (self.0.irq_restore)(state) };
47 }
48}
49
50#[derive(Clone, Copy, Debug, Eq, PartialEq)]
52#[repr(C)]
53pub struct ContextState {
54 preempt: usize,
55 irq: usize,
56}
57
58impl ContextState {
59 pub const fn new(preempt: usize, irq: usize) -> Self {
60 Self { preempt, irq }
61 }
62
63 pub const fn preempt(self) -> usize {
64 self.preempt
65 }
66
67 pub const fn irq(self) -> usize {
68 self.irq
69 }
70}
71
72pub fn context_enter(context: u8, operations: &ContextOperations) -> ContextState {
73 let backend = ExternalContext(operations);
74 match context {
75 CONTEXT_RAW => ContextState::new(0, 0),
76 CONTEXT_PREEMPT => ContextState::new(backend.preempt_enter(), 0),
77 CONTEXT_IRQSAVE => ContextState::new(0, backend.irq_save_and_disable()),
78 CONTEXT_PREEMPT_IRQSAVE => {
79 let (preempt, irq) = enter_preempt_irqsave(&backend);
80 ContextState::new(preempt, irq)
81 }
82 _ => panic!("unknown external lock context {context}"),
83 }
84}
85
86pub fn context_exit(context: u8, state: ContextState, operations: &ContextOperations) {
87 let backend = ExternalContext(operations);
88 match context {
89 CONTEXT_RAW => {}
90 CONTEXT_PREEMPT => backend.preempt_exit(state.preempt),
91 CONTEXT_IRQSAVE => backend.irq_restore(state.irq),
92 CONTEXT_PREEMPT_IRQSAVE => {
93 exit_preempt_irqsave((state.preempt, state.irq), &backend);
94 }
95 _ => panic!("unknown external lock context {context}"),
96 }
97}
98
99pub fn irq_return_preempt_enter(operations: &ContextOperations) -> usize {
100 ExternalContext(operations).preempt_enter()
101}
102
103pub unsafe fn irq_return_preempt_exit(state: usize, operations: &ContextOperations) {
108 ExternalContext(operations).preempt_exit_irq_return(state);
109}
110
111pub fn hardirq_enter(operations: &ContextOperations) {
112 (operations.hardirq_enter)();
113}
114
115pub fn hardirq_exit(operations: &ContextOperations) {
116 (operations.hardirq_exit)();
117}
118
119#[cfg(test)]
120mod tests {
121 use std::{sync::Mutex, vec::Vec};
122
123 use super::*;
124
125 const PREEMPT_TOKEN: usize = 0x1111;
126 const IRQ_TOKEN: usize = 0x2222;
127
128 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
129 enum Event {
130 PreemptEnter,
131 IrqDisable,
132 IrqRestore(usize),
133 PreemptExit(usize),
134 }
135
136 static TEST_LOCK: Mutex<()> = Mutex::new(());
137 static EVENTS: Mutex<Vec<Event>> = Mutex::new(Vec::new());
138
139 fn record(event: Event) {
140 EVENTS.lock().unwrap().push(event);
141 }
142
143 fn preempt_enter() -> usize {
144 record(Event::PreemptEnter);
145 PREEMPT_TOKEN
146 }
147
148 unsafe fn preempt_exit(state: usize) {
149 record(Event::PreemptExit(state));
150 }
151
152 unsafe fn preempt_exit_irq_return(_state: usize) {}
153
154 fn irq_save_and_disable() -> usize {
155 record(Event::IrqDisable);
156 IRQ_TOKEN
157 }
158
159 unsafe fn irq_restore(state: usize) {
160 record(Event::IrqRestore(state));
161 }
162
163 fn no_op() {}
164
165 fn operations() -> ContextOperations {
166 ContextOperations {
167 preempt_enter,
168 preempt_exit,
169 preempt_exit_irq_return,
170 irq_save_and_disable,
171 irq_restore,
172 hardirq_enter: no_op,
173 hardirq_exit: no_op,
174 }
175 }
176
177 fn take_events() -> Vec<Event> {
178 core::mem::take(&mut *EVENTS.lock().unwrap())
179 }
180
181 #[test]
182 fn combined_bridge_preserves_tokens_and_restores_irq_before_preempt() {
183 let _serial = TEST_LOCK.lock().unwrap();
184 take_events();
185
186 let state = context_enter(CONTEXT_PREEMPT_IRQSAVE, &operations());
187 assert_eq!(state, ContextState::new(PREEMPT_TOKEN, IRQ_TOKEN));
188 assert_eq!(take_events(), [Event::PreemptEnter, Event::IrqDisable]);
189
190 context_exit(CONTEXT_PREEMPT_IRQSAVE, state, &operations());
191 assert_eq!(
192 take_events(),
193 [
194 Event::IrqRestore(IRQ_TOKEN),
195 Event::PreemptExit(PREEMPT_TOKEN)
196 ]
197 );
198 }
199
200 #[test]
201 fn raw_bridge_does_not_invoke_callbacks() {
202 let _serial = TEST_LOCK.lock().unwrap();
203 take_events();
204
205 let state = context_enter(CONTEXT_RAW, &operations());
206 assert_eq!(state, ContextState::new(0, 0));
207 context_exit(CONTEXT_RAW, state, &operations());
208
209 assert!(take_events().is_empty());
210 }
211}