Skip to main content

rdif_block/
request.rs

1use alloc::boxed::Box;
2use core::{
3    marker::PhantomData,
4    ops::{Deref, DerefMut},
5};
6
7use dma_api::{CompletedDma, PreparedDma};
8
9use crate::{BlkError, DeviceInfo, QueueInfo, QueueLimits};
10
11#[repr(transparent)]
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct RequestId(usize);
14
15impl RequestId {
16    pub const fn new(id: usize) -> Self {
17        Self(id)
18    }
19}
20
21impl From<RequestId> for usize {
22    fn from(value: RequestId) -> Self {
23        value.0
24    }
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum RequestStatus {
29    Pending,
30    Complete,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum RequestOp {
35    Read,
36    Write,
37    Flush,
38    Discard,
39    WriteZeroes,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub struct RequestFlags(u32);
44
45impl RequestFlags {
46    pub const NONE: Self = Self(0);
47    pub const FUA: Self = Self(1 << 0);
48    pub const PREFLUSH: Self = Self(1 << 1);
49    pub const SYNC: Self = Self(1 << 2);
50    pub const META: Self = Self(1 << 3);
51    pub const POLLED: Self = Self(1 << 4);
52    pub const NOWAIT: Self = Self(1 << 5);
53    pub const ALL_KNOWN: Self = Self(
54        Self::FUA.bits()
55            | Self::PREFLUSH.bits()
56            | Self::SYNC.bits()
57            | Self::META.bits()
58            | Self::POLLED.bits()
59            | Self::NOWAIT.bits(),
60    );
61
62    pub const fn bits(self) -> u32 {
63        self.0
64    }
65
66    pub const fn is_empty(self) -> bool {
67        self.0 == 0
68    }
69
70    pub const fn contains(self, other: Self) -> bool {
71        (self.0 & other.0) == other.0
72    }
73
74    pub const fn intersects(self, other: Self) -> bool {
75        (self.0 & other.0) != 0
76    }
77
78    pub const fn unsupported_by(self, supported: Self) -> Self {
79        Self(self.0 & !supported.0)
80    }
81}
82
83impl core::ops::BitOr for RequestFlags {
84    type Output = Self;
85
86    fn bitor(self, rhs: Self) -> Self::Output {
87        Self(self.0 | rhs.0)
88    }
89}
90
91impl core::ops::BitOrAssign for RequestFlags {
92    fn bitor_assign(&mut self, rhs: Self) {
93        self.0 |= rhs.0;
94    }
95}
96
97impl Default for RequestFlags {
98    fn default() -> Self {
99        Self::NONE
100    }
101}
102
103#[derive(Clone, Copy)]
104pub struct Segment<'a> {
105    pub virt: *mut u8,
106    pub bus: u64,
107    pub len: usize,
108    _marker: PhantomData<&'a mut [u8]>,
109}
110
111impl<'a> Segment<'a> {
112    /// Creates a block I/O segment from caller-owned CPU and DMA addresses.
113    ///
114    /// # Safety
115    ///
116    /// `virt` must be valid for reads and writes of `len` bytes for the
117    /// whole request lifetime, and `bus` must be the DMA/bus address for the
118    /// same storage. The caller must keep the buffer and DMA mapping alive
119    /// until `poll_request` reports `RequestStatus::Complete`.
120    pub unsafe fn from_raw_parts(virt: *mut u8, bus: u64, len: usize) -> Self {
121        Self {
122            virt,
123            bus,
124            len,
125            _marker: PhantomData,
126        }
127    }
128}
129
130impl Deref for Segment<'_> {
131    type Target = [u8];
132
133    fn deref(&self) -> &Self::Target {
134        unsafe { core::slice::from_raw_parts(self.virt, self.len) }
135    }
136}
137
138impl DerefMut for Segment<'_> {
139    fn deref_mut(&mut self) -> &mut Self::Target {
140        unsafe { core::slice::from_raw_parts_mut(self.virt, self.len) }
141    }
142}
143
144pub type Buffer<'a> = Segment<'a>;
145
146pub struct Request<'a> {
147    pub op: RequestOp,
148    pub lba: u64,
149    pub block_count: u32,
150    pub segments: &'a mut [Segment<'a>],
151    pub flags: RequestFlags,
152}
153
154impl Request<'_> {
155    pub fn data_len(&self) -> usize {
156        self.segments.iter().map(|segment| segment.len).sum()
157    }
158
159    pub fn is_data_op(&self) -> bool {
160        matches!(self.op, RequestOp::Read | RequestOp::Write)
161    }
162}
163
164/// Block I/O request that moves DMA backing ownership into the queue.
165pub struct OwnedRequest {
166    pub op: RequestOp,
167    pub lba: u64,
168    pub block_count: u32,
169    pub data: Option<PreparedDma>,
170    pub flags: RequestFlags,
171}
172
173impl OwnedRequest {
174    pub fn data_len(&self) -> usize {
175        self.data.as_ref().map_or(0, |data| data.len().get())
176    }
177
178    pub fn is_data_op(&self) -> bool {
179        matches!(self.op, RequestOp::Read | RequestOp::Write)
180    }
181}
182
183/// Submit-side failure that returns request ownership to the caller.
184pub struct SubmitError {
185    pub error: BlkError,
186    request: Box<OwnedRequest>,
187}
188
189impl SubmitError {
190    pub fn new(error: BlkError, request: OwnedRequest) -> Self {
191        Self {
192            error,
193            request: Box::new(request),
194        }
195    }
196
197    pub fn into_request(self) -> OwnedRequest {
198        *self.request
199    }
200
201    pub fn request(&self) -> &OwnedRequest {
202        &self.request
203    }
204}
205
206/// One terminal request result with owned DMA backing returned to the runtime.
207pub struct CompletedRequest {
208    pub id: RequestId,
209    pub result: Result<(), BlkError>,
210    pub data: Option<CompletedDma>,
211}
212
213impl CompletedRequest {
214    pub const fn new(
215        id: RequestId,
216        result: Result<(), BlkError>,
217        data: Option<CompletedDma>,
218    ) -> Self {
219        Self { id, result, data }
220    }
221}
222
223/// Result of polling an owned queue request.
224pub enum RequestPoll {
225    Pending,
226    Ready(CompletedRequest),
227}
228
229/// Queue misuse/query failure.
230///
231/// This is intentionally separate from request I/O completion. Returning this
232/// error must not tell the runtime that the DMA backing is safe to recycle.
233#[derive(Debug, Clone, Copy, PartialEq, Eq)]
234pub enum PollError {
235    UnknownRequest,
236    WrongQueue,
237    DriverPoisoned,
238}
239
240impl From<PollError> for BlkError {
241    fn from(value: PollError) -> Self {
242        match value {
243            PollError::UnknownRequest | PollError::WrongQueue => BlkError::InvalidRequest,
244            PollError::DriverPoisoned => BlkError::Io,
245        }
246    }
247}
248
249pub fn validate_request(info: QueueInfo, request: &Request<'_>) -> Result<(), BlkError> {
250    validate_request_flags(info, request.flags)?;
251    validate_request_shape(info.device, info.limits, request)
252}
253
254pub fn validate_request_shape(
255    info: DeviceInfo,
256    limits: QueueLimits,
257    request: &Request<'_>,
258) -> Result<(), BlkError> {
259    if request.block_count == 0 && !matches!(request.op, RequestOp::Flush) {
260        return Err(BlkError::InvalidRequest);
261    }
262
263    if request.lba >= info.num_blocks
264        || request
265            .lba
266            .checked_add(request.block_count as u64)
267            .is_none_or(|end| end > info.num_blocks)
268    {
269        return Err(BlkError::InvalidBlockIndex(request.lba));
270    }
271
272    match request.op {
273        RequestOp::Read | RequestOp::Write => {
274            let expected = request
275                .block_count
276                .checked_mul(info.logical_block_size as u32)
277                .map(|len| len as usize)
278                .ok_or(BlkError::InvalidRequest)?;
279            if request.segments.is_empty()
280                || request.segments.len() > limits.max_segments
281                || request.data_len() != expected
282            {
283                return Err(BlkError::InvalidRequest);
284            }
285            if request
286                .segments
287                .iter()
288                .any(|segment| segment.len > limits.max_segment_size)
289            {
290                return Err(BlkError::InvalidRequest);
291            }
292        }
293        RequestOp::Flush => {
294            if !request.segments.is_empty() || request.block_count != 0 {
295                return Err(BlkError::InvalidRequest);
296            }
297            if !limits.supports_flush {
298                return Err(BlkError::NotSupported);
299            }
300        }
301        RequestOp::Discard => {
302            if !request.segments.is_empty() {
303                return Err(BlkError::InvalidRequest);
304            }
305            if !limits.supports_discard {
306                return Err(BlkError::NotSupported);
307            }
308        }
309        RequestOp::WriteZeroes => {
310            if !request.segments.is_empty() {
311                return Err(BlkError::InvalidRequest);
312            }
313            if !limits.supports_write_zeroes {
314                return Err(BlkError::NotSupported);
315            }
316        }
317    }
318
319    if request.block_count > limits.max_blocks_per_request {
320        return Err(BlkError::InvalidRequest);
321    }
322
323    Ok(())
324}
325
326pub fn validate_owned_request(info: QueueInfo, request: &OwnedRequest) -> Result<(), BlkError> {
327    validate_request_flags(info, request.flags)?;
328    validate_owned_request_shape(info.device, info.limits, request)
329}
330
331pub fn validate_owned_request_shape(
332    info: DeviceInfo,
333    limits: QueueLimits,
334    request: &OwnedRequest,
335) -> Result<(), BlkError> {
336    if request.block_count == 0 && !matches!(request.op, RequestOp::Flush) {
337        return Err(BlkError::InvalidRequest);
338    }
339
340    if request.lba >= info.num_blocks
341        || request
342            .lba
343            .checked_add(request.block_count as u64)
344            .is_none_or(|end| end > info.num_blocks)
345    {
346        return Err(BlkError::InvalidBlockIndex(request.lba));
347    }
348
349    match request.op {
350        RequestOp::Read | RequestOp::Write => {
351            let expected = request
352                .block_count
353                .checked_mul(info.logical_block_size as u32)
354                .map(|len| len as usize)
355                .ok_or(BlkError::InvalidRequest)?;
356            if request.data_len() != expected {
357                return Err(BlkError::InvalidRequest);
358            }
359            let Some(data) = &request.data else {
360                return Err(BlkError::InvalidRequest);
361            };
362            let segments = data.segments();
363            if segments.is_empty()
364                || segments.len() > limits.max_segments
365                || segments
366                    .iter()
367                    .any(|segment| segment.len.get() > limits.max_segment_size)
368            {
369                return Err(BlkError::InvalidRequest);
370            }
371        }
372        RequestOp::Flush => {
373            if request.data.is_some() || request.block_count != 0 {
374                return Err(BlkError::InvalidRequest);
375            }
376            if !limits.supports_flush {
377                return Err(BlkError::NotSupported);
378            }
379        }
380        RequestOp::Discard => {
381            if request.data.is_some() {
382                return Err(BlkError::InvalidRequest);
383            }
384            if !limits.supports_discard {
385                return Err(BlkError::NotSupported);
386            }
387        }
388        RequestOp::WriteZeroes => {
389            if request.data.is_some() {
390                return Err(BlkError::InvalidRequest);
391            }
392            if !limits.supports_write_zeroes {
393                return Err(BlkError::NotSupported);
394            }
395        }
396    }
397
398    if request.block_count > limits.max_blocks_per_request {
399        return Err(BlkError::InvalidRequest);
400    }
401
402    Ok(())
403}
404
405fn validate_request_flags(info: QueueInfo, flags: RequestFlags) -> Result<(), BlkError> {
406    let unknown = flags.unsupported_by(RequestFlags::ALL_KNOWN);
407    if !unknown.is_empty() {
408        return Err(BlkError::InvalidRequest);
409    }
410
411    let unsupported = flags.unsupported_by(info.limits.supported_flags);
412    if !unsupported.is_empty() {
413        return Err(BlkError::NotSupported);
414    }
415
416    if flags.intersects(RequestFlags::PREFLUSH) && !info.limits.supports_flush {
417        return Err(BlkError::NotSupported);
418    }
419
420    Ok(())
421}
422
423#[cfg(test)]
424mod tests {
425    use super::*;
426
427    #[test]
428    fn request_status_distinguishes_pending_from_errors() {
429        assert_eq!(RequestStatus::Pending, RequestStatus::Pending);
430        assert_ne!(RequestStatus::Pending, RequestStatus::Complete);
431    }
432
433    #[test]
434    fn segment_carries_cpu_and_dma_addresses() {
435        let mut bytes = [0x5a_u8; 4];
436        let segment = unsafe { Segment::from_raw_parts(bytes.as_mut_ptr(), 0x1000, bytes.len()) };
437
438        assert_eq!(segment.bus, 0x1000);
439        assert_eq!(&*segment, &[0x5a; 4]);
440    }
441
442    #[test]
443    fn request_shape_checks_lba_and_segments() {
444        let info = DeviceInfo::new(8, 512);
445        let limits = QueueLimits {
446            max_blocks_per_request: 8,
447            max_segment_size: 1024,
448            ..QueueLimits::simple(512, u64::MAX)
449        };
450        let mut bytes = [0_u8; 1024];
451        let segment = unsafe { Segment::from_raw_parts(bytes.as_mut_ptr(), 0x1000, bytes.len()) };
452        let mut segments = [segment];
453        let request = Request {
454            op: RequestOp::Read,
455            lba: 1,
456            block_count: 2,
457            segments: &mut segments,
458            flags: RequestFlags::NONE,
459        };
460
461        assert_eq!(validate_request_shape(info, limits, &request), Ok(()));
462    }
463
464    #[test]
465    fn request_shape_rejects_wrong_segment_size() {
466        let info = DeviceInfo::new(8, 512);
467        let limits = QueueLimits::simple(512, u64::MAX);
468        let mut bytes = [0_u8; 512];
469        let segment = unsafe { Segment::from_raw_parts(bytes.as_mut_ptr(), 0x1000, bytes.len()) };
470        let mut segments = [segment];
471        let request = Request {
472            op: RequestOp::Write,
473            lba: 1,
474            block_count: 2,
475            segments: &mut segments,
476            flags: RequestFlags::NONE,
477        };
478
479        assert_eq!(
480            validate_request_shape(info, limits, &request),
481            Err(BlkError::InvalidRequest)
482        );
483    }
484
485    fn queue_info_with(limits: QueueLimits) -> QueueInfo {
486        QueueInfo {
487            id: 0,
488            device: DeviceInfo::new(64, 512),
489            limits,
490        }
491    }
492
493    #[test]
494    fn request_validation_rejects_unsupported_flags() {
495        let info = queue_info_with(QueueLimits::simple(512, u64::MAX));
496        let mut bytes = [0_u8; 512];
497        let segment = unsafe { Segment::from_raw_parts(bytes.as_mut_ptr(), 0x1000, bytes.len()) };
498        let mut segments = [segment];
499        let request = Request {
500            op: RequestOp::Write,
501            lba: 0,
502            block_count: 1,
503            segments: &mut segments,
504            flags: RequestFlags::FUA,
505        };
506
507        assert_eq!(
508            validate_request(info, &request),
509            Err(BlkError::NotSupported)
510        );
511    }
512
513    #[test]
514    fn request_validation_rejects_unknown_flags() {
515        let info = queue_info_with(QueueLimits::simple(512, u64::MAX));
516        let mut bytes = [0_u8; 512];
517        let segment = unsafe { Segment::from_raw_parts(bytes.as_mut_ptr(), 0x1000, bytes.len()) };
518        let mut segments = [segment];
519        let request = Request {
520            op: RequestOp::Read,
521            lba: 0,
522            block_count: 1,
523            segments: &mut segments,
524            flags: RequestFlags(1 << 24),
525        };
526
527        assert_eq!(
528            validate_request(info, &request),
529            Err(BlkError::InvalidRequest)
530        );
531    }
532
533    #[test]
534    fn request_validation_accepts_supported_flags() {
535        let mut limits = QueueLimits::simple(512, u64::MAX);
536        limits.supported_flags = RequestFlags::FUA;
537        let info = queue_info_with(limits);
538        let mut bytes = [0_u8; 512];
539        let segment = unsafe { Segment::from_raw_parts(bytes.as_mut_ptr(), 0x1000, bytes.len()) };
540        let mut segments = [segment];
541        let request = Request {
542            op: RequestOp::Write,
543            lba: 0,
544            block_count: 1,
545            segments: &mut segments,
546            flags: RequestFlags::FUA,
547        };
548
549        assert_eq!(validate_request(info, &request), Ok(()));
550    }
551
552    #[test]
553    fn preflush_flag_requires_flush_support() {
554        let mut limits = QueueLimits::simple(512, u64::MAX);
555        limits.supported_flags = RequestFlags::PREFLUSH;
556        let info = queue_info_with(limits);
557        let mut bytes = [0_u8; 512];
558        let segment = unsafe { Segment::from_raw_parts(bytes.as_mut_ptr(), 0x1000, bytes.len()) };
559        let mut segments = [segment];
560        let request = Request {
561            op: RequestOp::Write,
562            lba: 0,
563            block_count: 1,
564            segments: &mut segments,
565            flags: RequestFlags::PREFLUSH,
566        };
567
568        assert_eq!(
569            validate_request(info, &request),
570            Err(BlkError::NotSupported)
571        );
572    }
573
574    #[test]
575    fn request_validation_rejects_transfer_larger_than_hard_block_limit() {
576        let info = queue_info_with(QueueLimits {
577            dma_mask: u64::MAX,
578            dma_domain: dma_api::DmaDomainId::legacy_global(),
579            dma_alignment: 512,
580            max_inflight: 1,
581            max_blocks_per_request: 2,
582            max_segments: 1,
583            max_segment_size: 4096,
584            supported_flags: RequestFlags::NONE,
585            supports_flush: false,
586            supports_discard: false,
587            supports_write_zeroes: false,
588        });
589        let mut bytes = [0_u8; 1536];
590        let segment = unsafe { Segment::from_raw_parts(bytes.as_mut_ptr(), 0x1000, bytes.len()) };
591        let mut segments = [segment];
592        let request = Request {
593            op: RequestOp::Write,
594            lba: 0,
595            block_count: 3,
596            segments: &mut segments,
597            flags: RequestFlags::NONE,
598        };
599
600        assert_eq!(
601            validate_request(info, &request),
602            Err(BlkError::InvalidRequest)
603        );
604    }
605}