Skip to main content

rdif_block/
irq.rs

1use alloc::vec::Vec;
2
3use crate::RequestId;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct IrqSourceInfo {
7    pub id: usize,
8    pub queues: IdList,
9}
10
11impl IrqSourceInfo {
12    pub const fn new(id: usize, queues: IdList) -> Self {
13        Self { id, queues }
14    }
15
16    pub const fn legacy(queues: IdList) -> Self {
17        Self { id: 0, queues }
18    }
19}
20
21pub type IrqSourceList = Vec<IrqSourceInfo>;
22
23pub trait IrqHandler: Send + 'static {
24    /// Handle a device interrupt in hard IRQ context.
25    ///
26    /// Implementations must acknowledge or clear the device-side interrupt
27    /// source before returning. The returned event is a stable hint for the OS
28    /// runtime; task context is still responsible for consuming completions and
29    /// completing block requests.
30    ///
31    /// Hard IRQ handlers must not call OS task, wake, or filesystem APIs, must
32    /// not copy DMA buffers for completed requests, and must not update an OS
33    /// block runtime pending table. Drivers that need to consume device queue
34    /// state to clear the interrupt should cache those completions internally
35    /// and return a queue-level event.
36    fn handle_irq(&mut self) -> Event;
37}
38
39#[repr(transparent)]
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub struct IdList(u64);
42
43impl IdList {
44    pub const fn none() -> Self {
45        Self(0)
46    }
47
48    pub const fn from_bits(bits: u64) -> Self {
49        Self(bits)
50    }
51
52    pub const fn bits(self) -> u64 {
53        self.0
54    }
55
56    pub fn contains(&self, id: usize) -> bool {
57        id < 64 && (self.0 & (1 << id)) != 0
58    }
59
60    pub fn insert(&mut self, id: usize) {
61        if id < 64 {
62            self.0 |= 1 << id;
63        }
64    }
65
66    pub fn remove(&mut self, id: usize) {
67        if id < 64 {
68            self.0 &= !(1 << id);
69        }
70    }
71
72    pub fn iter(&self) -> impl Iterator<Item = usize> {
73        (0..64).filter(move |i| self.contains(*i))
74    }
75}
76
77pub const MAX_COMPLETION_HINTS: usize = 8;
78pub const MAX_BATCH_COMPLETION_IDS: usize = 16;
79
80#[derive(Debug, Clone, Copy)]
81pub enum CompletionHint {
82    Queue {
83        queue_id: usize,
84    },
85    Request {
86        queue_id: usize,
87        request_id: RequestId,
88    },
89    Batch {
90        queue_id: usize,
91        ids: CompletionIds,
92    },
93}
94
95impl CompletionHint {
96    pub const fn queue_id(self) -> usize {
97        match self {
98            Self::Queue { queue_id }
99            | Self::Request { queue_id, .. }
100            | Self::Batch { queue_id, .. } => queue_id,
101        }
102    }
103}
104
105#[derive(Debug, Clone, Copy)]
106pub struct CompletionIds {
107    len: usize,
108    ids: [RequestId; MAX_BATCH_COMPLETION_IDS],
109}
110
111impl CompletionIds {
112    pub const fn new() -> Self {
113        Self {
114            len: 0,
115            ids: [RequestId::new(0); MAX_BATCH_COMPLETION_IDS],
116        }
117    }
118
119    pub fn push(&mut self, request_id: RequestId) -> bool {
120        if self.len == self.ids.len() {
121            return false;
122        }
123        self.ids[self.len] = request_id;
124        self.len += 1;
125        true
126    }
127
128    pub const fn len(&self) -> usize {
129        self.len
130    }
131
132    pub const fn is_empty(&self) -> bool {
133        self.len == 0
134    }
135
136    pub fn iter(&self) -> impl Iterator<Item = RequestId> + '_ {
137        self.ids[..self.len].iter().copied()
138    }
139}
140
141impl Default for CompletionIds {
142    fn default() -> Self {
143        Self::new()
144    }
145}
146
147#[derive(Debug, Clone, Copy)]
148pub struct CompletionList {
149    len: usize,
150    hints: [Option<CompletionHint>; MAX_COMPLETION_HINTS],
151}
152
153impl CompletionList {
154    pub const fn new() -> Self {
155        Self {
156            len: 0,
157            hints: [None; MAX_COMPLETION_HINTS],
158        }
159    }
160
161    pub fn push(&mut self, hint: CompletionHint) -> bool {
162        if self.len == self.hints.len() {
163            return false;
164        }
165        self.hints[self.len] = Some(hint);
166        self.len += 1;
167        true
168    }
169
170    pub const fn len(&self) -> usize {
171        self.len
172    }
173
174    pub const fn is_empty(&self) -> bool {
175        self.len == 0
176    }
177
178    pub fn iter(&self) -> impl Iterator<Item = CompletionHint> + '_ {
179        self.hints[..self.len]
180            .iter()
181            .filter_map(|hint| hint.as_ref().copied())
182    }
183}
184
185impl Default for CompletionList {
186    fn default() -> Self {
187        Self::new()
188    }
189}
190
191#[derive(Debug, Clone, Copy)]
192pub struct Event {
193    pub queues: IdList,
194    pub completions: CompletionList,
195}
196
197impl Event {
198    pub const fn none() -> Self {
199        Self {
200            queues: IdList::none(),
201            completions: CompletionList::new(),
202        }
203    }
204
205    pub const fn from_queue_bits(bits: u64) -> Self {
206        Self {
207            queues: IdList::from_bits(bits),
208            completions: CompletionList::new(),
209        }
210    }
211
212    pub fn from_hint(hint: CompletionHint) -> Self {
213        let mut event = Self::none();
214        event.push_hint(hint);
215        event
216    }
217
218    pub fn push_queue(&mut self, queue_id: usize) {
219        self.queues.insert(queue_id);
220        let _ = self.completions.push(CompletionHint::Queue { queue_id });
221    }
222
223    pub fn push_request(&mut self, queue_id: usize, request_id: RequestId) {
224        if !self.completions.push(CompletionHint::Request {
225            queue_id,
226            request_id,
227        }) {
228            self.queues.insert(queue_id);
229        }
230    }
231
232    pub fn push_hint(&mut self, hint: CompletionHint) {
233        if let CompletionHint::Queue { queue_id } = hint {
234            self.queues.insert(queue_id);
235        }
236        if !self.completions.push(hint) {
237            self.queues.insert(hint.queue_id());
238        }
239    }
240
241    pub fn is_empty(&self) -> bool {
242        self.queues.bits() == 0 && self.completions.is_empty()
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249
250    #[test]
251    fn irq_source_lists_queue_masks() {
252        let mut queues = IdList::none();
253        queues.insert(2);
254        let source = IrqSourceInfo::legacy(queues);
255
256        assert_eq!(source.id, 0);
257        assert!(source.queues.contains(2));
258    }
259
260    #[test]
261    fn completion_hints_preserve_queue_level_compatibility() {
262        let mut event = Event::none();
263        event.push_queue(3);
264        event.push_request(3, RequestId::new(7));
265
266        assert!(event.queues.contains(3));
267        assert_eq!(event.completions.len(), 2);
268    }
269
270    #[test]
271    fn completion_hint_overflow_falls_back_to_queue_bit() {
272        let mut event = Event::none();
273        for id in 0..(MAX_COMPLETION_HINTS + 1) {
274            event.push_request(5, RequestId::new(id));
275        }
276
277        assert_eq!(event.completions.len(), MAX_COMPLETION_HINTS);
278        assert!(event.queues.contains(5));
279    }
280
281    #[test]
282    fn queue_event_represents_driver_local_completion_ready() {
283        let event = Event::from_hint(CompletionHint::Queue { queue_id: 2 });
284
285        assert!(event.queues.contains(2));
286        assert_eq!(event.completions.len(), 1);
287    }
288}