Skip to main content

rdif_block/
interface.rs

1use alloc::{boxed::Box, vec::Vec};
2
3use crate::{
4    BlkError, DeviceInfo, DriverGeneric, IrqHandler, IrqSourceList, OwnedRequest, PollError,
5    QueueInfo, QueueLimits, Request, RequestId, RequestPoll, RequestStatus, SubmitError,
6};
7
8pub type BInterface = Box<dyn Interface>;
9pub type BQueue = Box<dyn IQueue>;
10pub type BOwnedQueue = Box<dyn IQueueOwned>;
11pub type BIrqHandler = Box<dyn IrqHandler>;
12
13pub trait Interface: DriverGeneric {
14    fn device_info(&self) -> DeviceInfo;
15
16    fn queue_limits(&self) -> QueueLimits;
17
18    fn create_queue(&mut self) -> Option<BQueue>;
19
20    fn create_owned_queue(&mut self) -> Option<QueueHandle> {
21        None
22    }
23
24    fn enable_irq(&self) {}
25
26    fn disable_irq(&self) {}
27
28    fn is_irq_enabled(&self) -> bool {
29        false
30    }
31
32    fn irq_sources(&self) -> IrqSourceList {
33        Vec::new()
34    }
35
36    fn take_irq_handler(&mut self, _source_id: usize) -> Option<BIrqHandler> {
37        None
38    }
39}
40
41pub trait CompletionSink {
42    fn complete(&mut self, request: RequestId, result: Result<(), BlkError>);
43}
44
45/// A request queue that owns DMA backing for every in-flight request.
46pub trait IQueueOwned: Send + 'static {
47    fn id(&self) -> usize;
48
49    fn info(&self) -> QueueInfo;
50
51    fn submit_request(&mut self, request: OwnedRequest) -> Result<RequestId, SubmitError>;
52
53    fn poll_request(&mut self, request: RequestId) -> Result<RequestPoll, PollError>;
54
55    fn cancel_request(&mut self, request: RequestId) -> Result<RequestPoll, PollError>;
56
57    fn shutdown(&mut self) {}
58}
59
60pub struct QueueHandle {
61    queue: Option<BOwnedQueue>,
62}
63
64impl QueueHandle {
65    pub fn new(queue: BOwnedQueue) -> Self {
66        Self { queue: Some(queue) }
67    }
68
69    pub fn id(&self) -> usize {
70        self.queue
71            .as_ref()
72            .expect("owned queue handle must contain queue")
73            .id()
74    }
75
76    pub fn info(&self) -> QueueInfo {
77        self.queue
78            .as_ref()
79            .expect("owned queue handle must contain queue")
80            .info()
81    }
82
83    pub fn submit_request(&mut self, request: OwnedRequest) -> Result<RequestId, SubmitError> {
84        self.queue
85            .as_mut()
86            .expect("owned queue handle must contain queue")
87            .submit_request(request)
88    }
89
90    pub fn poll_request(&mut self, request: RequestId) -> Result<RequestPoll, PollError> {
91        self.queue
92            .as_mut()
93            .expect("owned queue handle must contain queue")
94            .poll_request(request)
95    }
96
97    pub fn cancel_request(&mut self, request: RequestId) -> Result<RequestPoll, PollError> {
98        self.queue
99            .as_mut()
100            .expect("owned queue handle must contain queue")
101            .cancel_request(request)
102    }
103
104    pub fn shutdown(&mut self) {
105        if let Some(queue) = self.queue.as_mut() {
106            queue.shutdown();
107        }
108    }
109}
110
111impl Drop for QueueHandle {
112    fn drop(&mut self) {
113        self.shutdown();
114    }
115}
116
117/// A request queue for one block device hardware/software queue.
118///
119/// # Safety
120///
121/// Implementers may access `Request` segments after `submit_request` returns
122/// and until the matching `poll_request` returns `RequestStatus::Complete` or
123/// an error. They must not access any segment before `submit_request` is called
124/// or after completion/error has been reported, and request IDs must not alias
125/// two concurrently pending requests in a way that extends this lifetime.
126pub unsafe trait IQueue: Send + 'static {
127    fn id(&self) -> usize;
128
129    fn info(&self) -> QueueInfo;
130
131    fn submit_request(&mut self, request: Request<'_>) -> Result<RequestId, BlkError>;
132
133    fn poll_request(&mut self, request: RequestId) -> Result<RequestStatus, BlkError>;
134
135    /// Poll a set of in-flight requests and report observed terminal results.
136    ///
137    /// Implementers must report per-request completion or failure through
138    /// `sink.complete`. The return value describes the batch query itself:
139    /// `Err` means this poll attempt did not reliably observe request states
140    /// and must not be interpreted as terminal status for any request that was
141    /// not reported to the sink.
142    ///
143    /// `BlkError::Retry` is submit-side backpressure only. `poll_request` and
144    /// `poll_completions` must not return `Retry`; pending requests are
145    /// represented by omitting them from the sink in this method.
146    fn poll_completions(
147        &mut self,
148        requests: &[RequestId],
149        sink: &mut dyn CompletionSink,
150    ) -> Result<(), BlkError> {
151        for &request in requests {
152            match self.poll_request(request) {
153                Ok(RequestStatus::Pending) => {}
154                Ok(RequestStatus::Complete) => sink.complete(request, Ok(())),
155                Err(err) => sink.complete(request, Err(err)),
156            }
157        }
158        Ok(())
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use alloc::vec::Vec;
165
166    use super::*;
167    use crate::{DeviceInfo, Event, QueueLimits, RequestOp};
168
169    struct NoopIrq {
170        calls: usize,
171    }
172
173    impl IrqHandler for NoopIrq {
174        fn handle_irq(&mut self) -> Event {
175            self.calls += 1;
176            let mut event = Event::none();
177            event.queues.insert(1);
178            event
179        }
180    }
181
182    struct Queue;
183
184    // SAFETY: This test queue never stores request segments beyond
185    // `submit_request` and reports completion immediately.
186    unsafe impl IQueue for Queue {
187        fn id(&self) -> usize {
188            1
189        }
190
191        fn info(&self) -> QueueInfo {
192            QueueInfo {
193                id: 1,
194                device: DeviceInfo::new(8, 512),
195                limits: QueueLimits::simple(512, u64::MAX),
196            }
197        }
198
199        fn submit_request(&mut self, request: Request<'_>) -> Result<RequestId, BlkError> {
200            assert!(matches!(request.op, RequestOp::Read | RequestOp::Write));
201            Ok(RequestId::new(1))
202        }
203
204        fn poll_request(&mut self, _request: RequestId) -> Result<RequestStatus, BlkError> {
205            Ok(RequestStatus::Complete)
206        }
207    }
208
209    #[test]
210    fn block_api_uses_unified_queue_and_irq_events() {
211        fn assert_queue<T: IQueue>() {}
212        fn assert_irq_handler<T: IrqHandler>() {}
213
214        assert_queue::<Queue>();
215        assert_irq_handler::<NoopIrq>();
216
217        let mut irq = NoopIrq { calls: 0 };
218        let event = irq.handle_irq();
219        assert!(event.queues.contains(1));
220        assert_eq!(irq.calls, 1);
221    }
222
223    #[derive(Default)]
224    struct RecordingSink {
225        completions: Vec<(RequestId, Result<(), BlkError>)>,
226    }
227
228    impl CompletionSink for RecordingSink {
229        fn complete(&mut self, request_id: RequestId, result: Result<(), BlkError>) {
230            self.completions.push((request_id, result));
231        }
232    }
233
234    struct BatchQueue;
235
236    // SAFETY: This test queue never stores request segments and reports
237    // synthetic completion status from the requested ID only.
238    unsafe impl IQueue for BatchQueue {
239        fn id(&self) -> usize {
240            0
241        }
242
243        fn info(&self) -> QueueInfo {
244            QueueInfo {
245                id: 0,
246                device: DeviceInfo::new(8, 512),
247                limits: QueueLimits::simple(512, u64::MAX),
248            }
249        }
250
251        fn submit_request(&mut self, _request: Request<'_>) -> Result<RequestId, BlkError> {
252            Ok(RequestId::new(0))
253        }
254
255        fn poll_request(&mut self, request: RequestId) -> Result<RequestStatus, BlkError> {
256            match usize::from(request) {
257                1 => Ok(RequestStatus::Complete),
258                2 => Ok(RequestStatus::Pending),
259                3 => Err(BlkError::Io),
260                _ => Err(BlkError::InvalidRequest),
261            }
262        }
263    }
264
265    #[test]
266    fn default_batch_completion_polls_pending_ids_and_reports_terminal_results() {
267        let mut queue = BatchQueue;
268        let mut sink = RecordingSink::default();
269        let ids = [RequestId::new(1), RequestId::new(2), RequestId::new(3)];
270
271        queue.poll_completions(&ids, &mut sink).unwrap();
272
273        assert_eq!(
274            sink.completions,
275            [
276                (RequestId::new(1), Ok(())),
277                (RequestId::new(3), Err(BlkError::Io)),
278            ]
279        );
280    }
281
282    #[test]
283    fn simple_queue_limits_are_single_inflight() {
284        let limits = QueueLimits::simple(512, u64::MAX);
285
286        assert_eq!(limits.max_inflight, 1);
287    }
288
289    struct CountingIrq {
290        count: usize,
291    }
292
293    impl IrqHandler for CountingIrq {
294        fn handle_irq(&mut self) -> Event {
295            self.count += 1;
296            Event::from_queue_bits(self.count as u64)
297        }
298    }
299
300    #[test]
301    fn boxed_irq_handler_is_move_only_and_mutable() {
302        let mut handler: BIrqHandler = Box::new(CountingIrq { count: 0 });
303
304        assert_eq!(handler.handle_irq().queues.bits(), 1);
305        assert_eq!(handler.handle_irq().queues.bits(), 2);
306    }
307
308    struct OwnedQueue;
309
310    impl IQueueOwned for OwnedQueue {
311        fn id(&self) -> usize {
312            2
313        }
314
315        fn info(&self) -> QueueInfo {
316            QueueInfo {
317                id: 2,
318                device: DeviceInfo::new(8, 512),
319                limits: QueueLimits::simple(512, u64::MAX),
320            }
321        }
322
323        fn submit_request(&mut self, request: OwnedRequest) -> Result<RequestId, SubmitError> {
324            Err(SubmitError::new(BlkError::Retry, request))
325        }
326
327        fn poll_request(&mut self, _request: RequestId) -> Result<RequestPoll, PollError> {
328            Ok(RequestPoll::Pending)
329        }
330
331        fn cancel_request(&mut self, _request: RequestId) -> Result<RequestPoll, PollError> {
332            Err(PollError::UnknownRequest)
333        }
334    }
335
336    #[test]
337    fn owned_queue_submit_error_returns_request_ownership() {
338        let mut handle = QueueHandle::new(Box::new(OwnedQueue));
339        let request = OwnedRequest {
340            op: RequestOp::Flush,
341            lba: 0,
342            block_count: 0,
343            data: None,
344            flags: Default::default(),
345        };
346
347        let err = handle.submit_request(request).unwrap_err();
348
349        assert_eq!(err.error, BlkError::Retry);
350        assert!(matches!(err.request().op, RequestOp::Flush));
351    }
352}