Skip to main content

nvme_driver/
block.rs

1use alloc::{boxed::Box, sync::Arc, vec, vec::Vec};
2use core::{
3    any::Any,
4    cell::UnsafeCell,
5    hint::spin_loop,
6    sync::atomic::{AtomicBool, AtomicU16, AtomicU64, AtomicUsize, Ordering},
7};
8
9use dma_api::CoherentArray;
10use log::warn;
11use rdif_block::{
12    BlkError, CompletionSink, DeviceInfo, DriverGeneric, Event, IQueue, IdList, Interface,
13    IrqHandler, IrqSourceInfo, IrqSourceList, QueueInfo, QueueLimits, Request, RequestFlags,
14    RequestId, RequestOp, RequestStatus, validate_request,
15};
16
17use crate::{
18    Namespace, Nvme,
19    err::{Error as NvmeError, Result as NvmeResult},
20    queue::{CommandSet, NvmeCompletion, NvmeQueue as HardwareQueue},
21};
22
23const MAX_PRP_LIST_PAGES: usize = 1;
24const DEFAULT_QUEUE_DEPTH: usize = 64;
25
26struct NvmeBlockInner {
27    nvme: Nvme,
28    namespace: Namespace,
29}
30
31pub struct NvmeBlockDriver {
32    name: &'static str,
33    inner: Arc<NvmeBlockOwner>,
34    queue_depth: usize,
35}
36
37struct NvmeBlockOwner {
38    inner: UnsafeCell<NvmeBlockInner>,
39    queues: UnsafeCell<Vec<Arc<NvmeQueueCore>>>,
40    next_queue_id: AtomicUsize,
41    created_queue_bits: AtomicU64,
42    irq_enabled: AtomicBool,
43    irq_handler_taken_bits: AtomicU64,
44    irq_supported: bool,
45    msix_interrupts: bool,
46    interrupt_vectors: Vec<u16>,
47}
48
49impl NvmeBlockDriver {
50    pub fn from_nvme(mut nvme: Nvme) -> NvmeResult<Self> {
51        let namespace = nvme
52            .namespace_list()?
53            .into_iter()
54            .next()
55            .ok_or(NvmeError::Unknown("no active namespace found"))?;
56
57        Ok(Self::with_namespace("nvme", nvme, namespace))
58    }
59
60    pub fn from_nvme_with_queue_depth(mut nvme: Nvme, queue_depth: usize) -> NvmeResult<Self> {
61        let namespace = nvme
62            .namespace_list()?
63            .into_iter()
64            .next()
65            .ok_or(NvmeError::Unknown("no active namespace found"))?;
66
67        Ok(Self::with_namespace_and_queue_depth(
68            "nvme",
69            nvme,
70            namespace,
71            queue_depth,
72        ))
73    }
74
75    pub fn with_namespace(name: &'static str, nvme: Nvme, namespace: Namespace) -> Self {
76        Self::with_namespace_and_queue_depth(name, nvme, namespace, DEFAULT_QUEUE_DEPTH)
77    }
78
79    pub fn with_namespace_and_queue_depth(
80        name: &'static str,
81        nvme: Nvme,
82        namespace: Namespace,
83        queue_depth: usize,
84    ) -> Self {
85        let irq_supported = nvme.io_queue_interrupts_enabled();
86        let msix_interrupts = nvme.msix_interrupts_enabled();
87        let interrupt_vectors = nvme.interrupt_vectors().to_vec();
88        Self {
89            name,
90            inner: Arc::new(NvmeBlockOwner {
91                inner: UnsafeCell::new(NvmeBlockInner { nvme, namespace }),
92                queues: UnsafeCell::new(Vec::new()),
93                next_queue_id: AtomicUsize::new(0),
94                created_queue_bits: AtomicU64::new(0),
95                irq_enabled: AtomicBool::new(false),
96                irq_handler_taken_bits: AtomicU64::new(0),
97                irq_supported,
98                msix_interrupts,
99                interrupt_vectors,
100            }),
101            queue_depth: queue_depth.max(1),
102        }
103    }
104
105    pub fn namespace(&self) -> Namespace {
106        self.inner.with_mut(|inner| inner.namespace)
107    }
108
109    pub fn into_interface(self) -> Self {
110        self
111    }
112
113    fn device_info_for(&self) -> DeviceInfo {
114        self.inner
115            .with_mut(|inner| device_info(self.name, inner.namespace))
116    }
117
118    fn limits_for(&self) -> QueueLimits {
119        self.inner.with_mut(|inner| {
120            limits(
121                inner.nvme.dma_mask(),
122                inner.nvme.page_size(),
123                inner.nvme.max_transfer_bytes(),
124                inner.namespace,
125                self.queue_depth,
126            )
127        })
128    }
129}
130
131// SAFETY: RDIF queue ownership removes task-side sharing of an IO queue. IRQ
132// sharing is mediated by per-queue `NvmeQueueCore` claim guards, and the owner
133// keeps the controller and MMIO mapping alive until all queues are dropped.
134unsafe impl Send for NvmeBlockOwner {}
135
136// SAFETY: Mutable controller access is scoped through `with_mut` during queue
137// creation and namespace queries. The queue registry is populated before the
138// IRQ handler is taken and then read-only for the handler lifetime.
139unsafe impl Sync for NvmeBlockOwner {}
140
141impl NvmeBlockOwner {
142    fn with_mut<R>(&self, f: impl FnOnce(&mut NvmeBlockInner) -> R) -> R {
143        let inner = unsafe { &mut *self.inner.get() };
144        f(inner)
145    }
146
147    fn register_queue(&self, queue: Arc<NvmeQueueCore>) {
148        let queues = unsafe { &mut *self.queues.get() };
149        queues.push(queue);
150    }
151
152    fn queues(&self) -> &[Arc<NvmeQueueCore>] {
153        unsafe { &*self.queues.get() }
154    }
155
156    fn source_queue_bits(&self, source_id: usize, queue_bits: u64) -> u64 {
157        source_queue_bits(
158            self.msix_interrupts,
159            &self.interrupt_vectors,
160            source_id,
161            queue_bits,
162        )
163    }
164
165    fn irq_sources_from_queue_bits(&self, queue_bits: u64) -> IrqSourceList {
166        irq_sources_from_queue_bits(self.msix_interrupts, &self.interrupt_vectors, queue_bits)
167    }
168
169    fn unique_interrupt_vectors(&self) -> Vec<u16> {
170        unique_interrupt_vectors(&self.interrupt_vectors)
171    }
172}
173
174fn vector_for_queue(msix_interrupts: bool, vectors: &[u16], queue_id: usize) -> Option<u16> {
175    if msix_interrupts {
176        vectors.get(queue_id).copied()
177    } else {
178        Some(0)
179    }
180}
181
182fn source_queue_bits(
183    msix_interrupts: bool,
184    vectors: &[u16],
185    source_id: usize,
186    queue_bits: u64,
187) -> u64 {
188    if !msix_interrupts {
189        return if source_id == 0 { queue_bits } else { 0 };
190    }
191
192    let mut bits = 0;
193    for queue_id in 0..u64::BITS as usize {
194        if queue_bits & (1 << queue_id) == 0 {
195            continue;
196        }
197        if vector_for_queue(msix_interrupts, vectors, queue_id) == Some(source_id as u16) {
198            bits |= 1 << queue_id;
199        }
200    }
201    bits
202}
203
204fn irq_sources_from_queue_bits(
205    msix_interrupts: bool,
206    vectors: &[u16],
207    queue_bits: u64,
208) -> IrqSourceList {
209    if !msix_interrupts {
210        return vec![IrqSourceInfo::legacy(IdList::from_bits(queue_bits))];
211    }
212
213    let mut sources = Vec::new();
214    for vector in unique_interrupt_vectors(vectors) {
215        let queues = source_queue_bits(msix_interrupts, vectors, usize::from(vector), queue_bits);
216        if queues != 0 {
217            sources.push(IrqSourceInfo::new(
218                usize::from(vector),
219                IdList::from_bits(queues),
220            ));
221        }
222    }
223    sources
224}
225
226fn unique_interrupt_vectors(vectors: &[u16]) -> Vec<u16> {
227    let mut unique = Vec::new();
228    for vector in vectors {
229        if !unique.contains(vector) {
230            unique.push(*vector);
231        }
232    }
233    unique
234}
235
236impl DriverGeneric for NvmeBlockDriver {
237    fn name(&self) -> &str {
238        self.name
239    }
240
241    fn raw_any(&self) -> Option<&dyn Any> {
242        Some(self)
243    }
244
245    fn raw_any_mut(&mut self) -> Option<&mut dyn Any> {
246        Some(self)
247    }
248}
249
250impl Interface for NvmeBlockDriver {
251    fn device_info(&self) -> DeviceInfo {
252        self.device_info_for()
253    }
254
255    fn queue_limits(&self) -> QueueLimits {
256        self.limits_for()
257    }
258
259    fn create_queue(&mut self) -> Option<Box<dyn IQueue>> {
260        let id = self.inner.next_queue_id.fetch_add(1, Ordering::Relaxed);
261        if id >= u64::BITS as usize {
262            return None;
263        }
264
265        let queue = self.inner.with_mut(|inner| {
266            let queue = inner.nvme.take_io_queue(id)?;
267            let depth = self.queue_depth.min(queue.depth().saturating_sub(1).max(1));
268            let prp_lists = alloc_prp_lists(&inner.nvme, depth).ok()?;
269            Some(NvmeQueueCore::new(
270                id,
271                depth,
272                self.name,
273                inner.namespace,
274                inner.nvme.dma_mask(),
275                inner.nvme.page_size(),
276                inner.nvme.max_transfer_bytes(),
277                queue,
278                prp_lists,
279            ))
280        })?;
281
282        self.inner.register_queue(queue.clone());
283        self.inner
284            .created_queue_bits
285            .fetch_or(1 << id, Ordering::Release);
286        Some(Box::new(NvmeBlockQueue { core: queue }))
287    }
288
289    fn enable_irq(&self) {
290        if !self.inner.irq_supported {
291            return;
292        }
293        self.inner.with_mut(|inner| {
294            for vector in self.inner.unique_interrupt_vectors() {
295                inner.nvme.unmask_interrupt_vector(u32::from(vector));
296            }
297        });
298        self.inner.irq_enabled.store(true, Ordering::Release);
299    }
300
301    fn disable_irq(&self) {
302        if !self.inner.irq_supported {
303            return;
304        }
305        self.inner.with_mut(|inner| {
306            for vector in self.inner.unique_interrupt_vectors() {
307                inner.nvme.mask_interrupt_vector(u32::from(vector));
308            }
309        });
310        self.inner.irq_enabled.store(false, Ordering::Release);
311    }
312
313    fn is_irq_enabled(&self) -> bool {
314        self.inner.irq_supported && self.inner.irq_enabled.load(Ordering::Acquire)
315    }
316
317    fn irq_sources(&self) -> IrqSourceList {
318        let queue_bits = self.inner.created_queue_bits.load(Ordering::Acquire);
319        if !self.inner.irq_supported || queue_bits == 0 {
320            return Vec::new();
321        }
322        self.inner.irq_sources_from_queue_bits(queue_bits)
323    }
324
325    fn take_irq_handler(&mut self, source_id: usize) -> Option<Box<dyn IrqHandler>> {
326        if !self.inner.irq_supported || source_id >= u64::BITS as usize {
327            return None;
328        }
329        let queue_bits = self.inner.source_queue_bits(
330            source_id,
331            self.inner.created_queue_bits.load(Ordering::Acquire),
332        );
333        if queue_bits == 0 {
334            return None;
335        }
336        let bit = 1_u64 << source_id;
337        if self
338            .inner
339            .irq_handler_taken_bits
340            .fetch_or(bit, Ordering::AcqRel)
341            & bit
342            != 0
343        {
344            return None;
345        }
346        Some(Box::new(NvmeBlockIrqHandler {
347            owner: self.inner.clone(),
348            source_id,
349        }))
350    }
351}
352
353struct NvmeBlockIrqHandler {
354    owner: Arc<NvmeBlockOwner>,
355    source_id: usize,
356}
357
358impl IrqHandler for NvmeBlockIrqHandler {
359    fn handle_irq(&mut self) -> Event {
360        if !self.owner.irq_enabled.load(Ordering::Acquire) {
361            return Event::none();
362        }
363        let mut event = Event::none();
364        let source_queue_bits = self.owner.source_queue_bits(
365            self.source_id,
366            self.owner.created_queue_bits.load(Ordering::Acquire),
367        );
368        for queue in self.owner.queues() {
369            if source_queue_bits & (1 << queue.id()) == 0 {
370                continue;
371            }
372            if queue.drain_irq_completions() {
373                event.push_queue(queue.id());
374            }
375        }
376        event
377    }
378}
379
380struct NvmeBlockQueue {
381    core: Arc<NvmeQueueCore>,
382}
383
384struct NvmeQueueCore {
385    id: usize,
386    name: &'static str,
387    namespace: Namespace,
388    dma_mask: u64,
389    page_size: usize,
390    max_transfer_bytes: Option<usize>,
391    depth: usize,
392    queue: UnsafeCell<HardwareQueue>,
393    state: UnsafeCell<NvmeQueueState>,
394    completion_cache: CompletionCache,
395    state_claimed: AtomicBool,
396    cq_claimed: AtomicBool,
397}
398
399struct NvmeQueueState {
400    slots: Vec<RequestSlot>,
401    free_cids: Vec<usize>,
402    free_prp_lists: Vec<CoherentArray<u64>>,
403}
404
405struct RequestSlot {
406    state: SlotState,
407    prp_list: Option<CoherentArray<u64>>,
408}
409
410#[derive(Clone, Copy, Debug, PartialEq, Eq)]
411enum SlotState {
412    Free,
413    Pending,
414    Complete,
415    Failed,
416}
417
418#[derive(Clone, Copy, Debug, PartialEq, Eq)]
419struct CachedCompletion {
420    cid: usize,
421    status: CompletionStatus,
422}
423
424#[derive(Clone, Copy, Debug, PartialEq, Eq)]
425struct CompletionStatus {
426    success: bool,
427    raw_status: u16,
428    result: u64,
429}
430
431struct CompletionCache {
432    entries: Vec<CompletionCacheEntry>,
433}
434
435struct CompletionCacheEntry {
436    ready: AtomicBool,
437    success: AtomicBool,
438    raw_status: AtomicU16,
439    result: AtomicU64,
440}
441
442struct PrpMapping {
443    prp1: u64,
444    prp2: u64,
445    prp_list: Option<CoherentArray<u64>>,
446}
447
448impl NvmeQueueCore {
449    #[allow(clippy::too_many_arguments)]
450    fn new(
451        id: usize,
452        depth: usize,
453        name: &'static str,
454        namespace: Namespace,
455        dma_mask: u64,
456        page_size: usize,
457        max_transfer_bytes: Option<usize>,
458        queue: HardwareQueue,
459        prp_lists: Vec<CoherentArray<u64>>,
460    ) -> Arc<Self> {
461        let mut slots = Vec::with_capacity(depth + 1);
462        slots.resize_with(depth + 1, || RequestSlot {
463            state: SlotState::Free,
464            prp_list: None,
465        });
466        let free_cids = (1..=depth).rev().collect();
467
468        Arc::new(Self {
469            id,
470            name,
471            namespace,
472            dma_mask,
473            page_size,
474            max_transfer_bytes,
475            depth,
476            queue: UnsafeCell::new(queue),
477            state: UnsafeCell::new(NvmeQueueState {
478                slots,
479                free_cids,
480                free_prp_lists: prp_lists,
481            }),
482            completion_cache: CompletionCache::new(depth + 1),
483            state_claimed: AtomicBool::new(false),
484            cq_claimed: AtomicBool::new(false),
485        })
486    }
487
488    const fn id(&self) -> usize {
489        self.id
490    }
491
492    fn queue_info(&self) -> QueueInfo {
493        QueueInfo {
494            id: self.id,
495            device: device_info(self.name, self.namespace),
496            limits: limits(
497                self.dma_mask,
498                self.page_size,
499                self.max_transfer_bytes,
500                self.namespace,
501                self.depth,
502            ),
503        }
504    }
505
506    fn with_claim<R>(&self, f: impl FnOnce(&mut NvmeQueueState) -> R) -> R {
507        while self
508            .state_claimed
509            .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
510            .is_err()
511        {
512            spin_loop();
513        }
514        let state = unsafe { &mut *self.state.get() };
515        let result = f(state);
516        self.state_claimed.store(false, Ordering::Release);
517        result
518    }
519
520    fn with_cq_claim<R>(&self, f: impl FnOnce(&HardwareQueue) -> R) -> R {
521        while self
522            .cq_claimed
523            .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
524            .is_err()
525        {
526            spin_loop();
527        }
528        let queue = unsafe { &*self.queue.get() };
529        let result = f(queue);
530        self.cq_claimed.store(false, Ordering::Release);
531        result
532    }
533
534    fn try_with_cq_claim<R>(&self, f: impl FnOnce(&HardwareQueue) -> R) -> Option<R> {
535        if self
536            .cq_claimed
537            .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
538            .is_err()
539        {
540            return None;
541        }
542        let queue = unsafe { &*self.queue.get() };
543        let result = f(queue);
544        self.cq_claimed.store(false, Ordering::Release);
545        Some(result)
546    }
547
548    fn drain_irq_completions(&self) -> bool {
549        self.try_with_cq_claim(drain_hardware_completions_to_vec)
550            .map(|completions| self.cache_completions(completions))
551            .unwrap_or(true)
552    }
553
554    fn drain_completions(&self) -> bool {
555        let completions = self.with_cq_claim(drain_hardware_completions_to_vec);
556        self.cache_completions(completions)
557    }
558
559    fn cache_completions(&self, completions: Vec<CachedCompletion>) -> bool {
560        if completions.is_empty() {
561            return false;
562        }
563        self.completion_cache.extend(completions);
564        true
565    }
566}
567
568// SAFETY: Slot, CID, and completion-cache access is serialized through
569// `state_claimed`; hardware CQ access is serialized through `cq_claimed`. SQ
570// submission is only driven by the single RDIF queue owner. The MMIO mapping
571// and DMA buffers outlive this core through the owner/interface lifetime.
572unsafe impl Send for NvmeQueueCore {}
573
574// SAFETY: Shared references are used by task context and hard IRQ context, but
575// shared mutable state is guarded as described in the `Send` impl.
576unsafe impl Sync for NvmeQueueCore {}
577
578impl NvmeQueueState {
579    fn alloc_cid(&mut self) -> Result<usize, BlkError> {
580        self.free_cids.pop().ok_or(BlkError::Retry)
581    }
582
583    fn free_cid(&mut self, cid: usize) {
584        if cid < self.slots.len() {
585            if let Some(prp_list) = self.slots[cid].prp_list.take() {
586                self.free_prp_lists.push(prp_list);
587            }
588            self.slots[cid].state = SlotState::Free;
589            self.free_cids.push(cid);
590        }
591    }
592
593    fn build_command(
594        &mut self,
595        namespace: Namespace,
596        page_size: usize,
597        cid: usize,
598        request: &Request<'_>,
599    ) -> Result<CommandSet, BlkError> {
600        let cid = u16::try_from(cid).map_err(|_| BlkError::InvalidRequest)?;
601        match request.op {
602            RequestOp::Read | RequestOp::Write => {
603                let prp = self.build_prp_mapping(page_size, request)?;
604                let command = match request.op {
605                    RequestOp::Read => CommandSet::nvm_cmd_read_with_cid(
606                        namespace.id,
607                        prp.prp1,
608                        prp.prp2,
609                        request.lba,
610                        request.block_count,
611                        cid,
612                    ),
613                    RequestOp::Write => CommandSet::nvm_cmd_write_with_cid(
614                        namespace.id,
615                        prp.prp1,
616                        prp.prp2,
617                        request.lba,
618                        request.block_count,
619                        cid,
620                    ),
621                    _ => unreachable!(),
622                };
623                self.slots[usize::from(cid)].prp_list = prp.prp_list;
624                Ok(command)
625            }
626            RequestOp::Flush => Ok(CommandSet::nvm_cmd_flush_with_cid(namespace.id, cid)),
627            RequestOp::Discard | RequestOp::WriteZeroes => Err(BlkError::NotSupported),
628        }
629    }
630
631    fn build_prp_mapping(
632        &mut self,
633        page_size: usize,
634        request: &Request<'_>,
635    ) -> Result<PrpMapping, BlkError> {
636        let mut prps = PrpPageAccumulator::new();
637        for segment in request.segments.iter() {
638            prps.push_segment(segment.bus, segment.len, page_size)?;
639        }
640        let pages = prps.into_pages();
641        let prp1 = *pages.first().ok_or(BlkError::InvalidRequest)?;
642        let prp2 = match pages.len() {
643            1 => 0,
644            2 => pages[1],
645            _ => {
646                let list_entries = page_size / core::mem::size_of::<u64>();
647                if pages.len() - 1 > list_entries * MAX_PRP_LIST_PAGES {
648                    return Err(BlkError::InvalidRequest);
649                }
650                let mut list = self.free_prp_lists.pop().ok_or(BlkError::Retry)?;
651                for entry in 0..list_entries {
652                    list.set_cpu(entry, 0);
653                }
654                for (entry, addr) in pages[1..].iter().copied().enumerate() {
655                    list.set_cpu(entry, addr);
656                }
657                let addr = list.dma_addr().as_u64();
658                return Ok(PrpMapping {
659                    prp1,
660                    prp2: addr,
661                    prp_list: Some(list),
662                });
663            }
664        };
665        Ok(PrpMapping {
666            prp1,
667            prp2,
668            prp_list: None,
669        })
670    }
671
672    fn consume_cached_completions(&mut self, queue_id: usize, cache: &CompletionCache) -> usize {
673        cache.drain_into_slots(queue_id, &mut self.slots)
674    }
675}
676
677fn drain_hardware_completions_to_vec(queue: &HardwareQueue) -> Vec<CachedCompletion> {
678    let mut completions = Vec::new();
679    while let Some(completion) = queue.poll_completion() {
680        completions.push(CachedCompletion::from(completion));
681    }
682    completions
683}
684
685impl CompletionCache {
686    fn new(capacity: usize) -> Self {
687        let mut entries = Vec::with_capacity(capacity);
688        entries.resize_with(capacity, CompletionCacheEntry::new);
689        Self { entries }
690    }
691
692    fn extend(&self, completions: Vec<CachedCompletion>) {
693        for completion in completions {
694            self.record(completion);
695        }
696    }
697
698    fn record(&self, completion: CachedCompletion) {
699        let Some(entry) = self.entries.get(completion.cid) else {
700            return;
701        };
702        entry
703            .success
704            .store(completion.status.success, Ordering::Relaxed);
705        entry
706            .raw_status
707            .store(completion.status.raw_status, Ordering::Relaxed);
708        entry
709            .result
710            .store(completion.status.result, Ordering::Relaxed);
711        entry.ready.store(true, Ordering::Release);
712    }
713
714    fn drain_into_slots(&self, queue_id: usize, slots: &mut [RequestSlot]) -> usize {
715        let mut consumed = 0;
716        for (cid, entry) in self.entries.iter().enumerate() {
717            if !entry.ready.swap(false, Ordering::AcqRel) {
718                continue;
719            }
720            let Some(slot) = slots.get_mut(cid) else {
721                continue;
722            };
723            let status = CompletionStatus {
724                success: entry.success.load(Ordering::Relaxed),
725                raw_status: entry.raw_status.load(Ordering::Relaxed),
726                result: entry.result.load(Ordering::Relaxed),
727            };
728            slot.state = if status.success {
729                SlotState::Complete
730            } else {
731                warn!(
732                    "nvme queue {} request {} failed: status={:#x}, result={:#x}",
733                    queue_id, cid, status.raw_status, status.result
734                );
735                SlotState::Failed
736            };
737            consumed += 1;
738        }
739        consumed
740    }
741}
742
743impl CompletionCacheEntry {
744    fn new() -> Self {
745        Self {
746            ready: AtomicBool::new(false),
747            success: AtomicBool::new(false),
748            raw_status: AtomicU16::new(0),
749            result: AtomicU64::new(0),
750        }
751    }
752}
753
754impl From<NvmeCompletion> for CachedCompletion {
755    fn from(completion: NvmeCompletion) -> Self {
756        Self {
757            cid: usize::from(completion.command_id),
758            status: CompletionStatus {
759                success: completion.status.is_success(),
760                raw_status: completion.status.0,
761                result: completion.result,
762            },
763        }
764    }
765}
766
767// SAFETY: NVMe queues may access submitted request DMA segments until the
768// matching completion is reclaimed by `poll_request`. Slots are freed only
769// after completion/error, and no segment pointers are accessed after that.
770unsafe impl IQueue for NvmeBlockQueue {
771    fn id(&self) -> usize {
772        self.core.id()
773    }
774
775    fn info(&self) -> QueueInfo {
776        self.core.queue_info()
777    }
778
779    fn submit_request(&mut self, request: Request<'_>) -> Result<RequestId, BlkError> {
780        let info = self.core.queue_info();
781        validate_request(info, &request)?;
782        let namespace = self.core.namespace;
783        let page_size = self.core.page_size;
784        let queue_id = self.core.id();
785
786        self.core.drain_completions();
787        self.core.with_claim(|state| {
788            state.consume_cached_completions(queue_id, &self.core.completion_cache);
789
790            let cid = state.alloc_cid()?;
791            let command = match state.build_command(namespace, page_size, cid, &request) {
792                Ok(command) => command,
793                Err(err) => {
794                    state.free_cid(cid);
795                    return Err(err);
796                }
797            };
798            state.slots[cid].state = SlotState::Pending;
799            let queue = unsafe { &*self.core.queue.get() };
800            queue.submit_io_data(command);
801            Ok(RequestId::new(cid))
802        })
803    }
804
805    fn poll_request(&mut self, request: RequestId) -> Result<RequestStatus, BlkError> {
806        let queue_id = self.core.id();
807        self.core.drain_completions();
808        self.core.with_claim(|state| {
809            state.consume_cached_completions(queue_id, &self.core.completion_cache);
810
811            let cid = usize::from(request);
812            match state.slots.get(cid).map(|slot| slot.state) {
813                Some(SlotState::Pending) => Ok(RequestStatus::Pending),
814                Some(SlotState::Complete) => {
815                    state.free_cid(cid);
816                    Ok(RequestStatus::Complete)
817                }
818                Some(SlotState::Failed) => {
819                    state.free_cid(cid);
820                    Err(BlkError::Io)
821                }
822                Some(SlotState::Free) | None => Err(BlkError::InvalidRequest),
823            }
824        })
825    }
826
827    fn poll_completions(
828        &mut self,
829        requests: &[RequestId],
830        sink: &mut dyn CompletionSink,
831    ) -> Result<(), BlkError> {
832        let queue_id = self.core.id();
833        self.core.drain_completions();
834        self.core.with_claim(|state| {
835            state.consume_cached_completions(queue_id, &self.core.completion_cache);
836
837            for &request in requests {
838                let cid = usize::from(request);
839                match state.slots.get(cid).map(|slot| slot.state) {
840                    Some(SlotState::Pending) => {}
841                    Some(SlotState::Complete) => {
842                        state.free_cid(cid);
843                        sink.complete(request, Ok(()));
844                    }
845                    Some(SlotState::Failed) => {
846                        state.free_cid(cid);
847                        sink.complete(request, Err(BlkError::Io));
848                    }
849                    Some(SlotState::Free) | None => {
850                        sink.complete(request, Err(BlkError::InvalidRequest))
851                    }
852                }
853            }
854            Ok(())
855        })
856    }
857}
858
859fn alloc_prp_lists(nvme: &Nvme, depth: usize) -> NvmeResult<Vec<CoherentArray<u64>>> {
860    let mut lists = Vec::with_capacity(depth);
861    for _ in 0..depth {
862        lists.push(nvme.alloc_prp_list()?);
863    }
864    Ok(lists)
865}
866
867#[derive(Default)]
868struct PrpPageAccumulator {
869    pages: Vec<u64>,
870    last_end: Option<u64>,
871    current_page_end: Option<u64>,
872}
873
874impl PrpPageAccumulator {
875    const fn new() -> Self {
876        Self {
877            pages: Vec::new(),
878            last_end: None,
879            current_page_end: None,
880        }
881    }
882
883    fn into_pages(self) -> Vec<u64> {
884        self.pages
885    }
886
887    fn push_segment(&mut self, addr: u64, len: usize, page_size: usize) -> Result<(), BlkError> {
888        if page_size == 0 || len == 0 {
889            return Err(BlkError::InvalidRequest);
890        }
891        let page_size = u64::try_from(page_size).map_err(|_| BlkError::InvalidRequest)?;
892        let end = addr
893            .checked_add(u64::try_from(len).map_err(|_| BlkError::InvalidRequest)?)
894            .ok_or(BlkError::InvalidRequest)?;
895        let mut cursor = addr;
896
897        while cursor < end {
898            self.ensure_page_entry(cursor, page_size)?;
899            let page_end = self.current_page_end.ok_or(BlkError::InvalidRequest)?;
900            let chunk_end = page_end.min(end);
901            if chunk_end <= cursor {
902                return Err(BlkError::InvalidRequest);
903            }
904            cursor = chunk_end;
905            self.last_end = Some(cursor);
906        }
907
908        Ok(())
909    }
910
911    fn ensure_page_entry(&mut self, cursor: u64, page_size: u64) -> Result<(), BlkError> {
912        let Some(last_end) = self.last_end else {
913            self.push_page(cursor, page_size)?;
914            return Ok(());
915        };
916        let current_page_end = self.current_page_end.ok_or(BlkError::InvalidRequest)?;
917
918        if cursor < last_end {
919            return Err(BlkError::InvalidRequest);
920        }
921        if cursor == last_end && cursor < current_page_end {
922            return Ok(());
923        }
924        if cursor != last_end && last_end != current_page_end {
925            return Err(BlkError::InvalidRequest);
926        }
927        if !cursor.is_multiple_of(page_size) {
928            return Err(BlkError::InvalidRequest);
929        }
930        self.push_page(cursor, page_size)
931    }
932
933    fn push_page(&mut self, addr: u64, page_size: u64) -> Result<(), BlkError> {
934        let page_base = addr / page_size * page_size;
935        let page_end = page_base
936            .checked_add(page_size)
937            .ok_or(BlkError::InvalidRequest)?;
938        self.pages.push(addr);
939        self.current_page_end = Some(page_end);
940        Ok(())
941    }
942}
943
944fn device_info(name: &'static str, namespace: Namespace) -> DeviceInfo {
945    DeviceInfo {
946        name: Some(name),
947        model: Some("nvme"),
948        ..DeviceInfo::new(namespace.lba_count as u64, namespace.lba_size)
949    }
950}
951
952fn limits(
953    dma_mask: u64,
954    page_size: usize,
955    controller_max_transfer_bytes: Option<usize>,
956    namespace: Namespace,
957    max_inflight: usize,
958) -> QueueLimits {
959    let lba_size = namespace.lba_size.max(1);
960    let dma_alignment = page_size.max(lba_size);
961    let prp_entries = page_size / core::mem::size_of::<u64>();
962    let prp_capacity_bytes = page_size.saturating_mul(prp_entries + 1);
963    let max_bytes = controller_max_transfer_bytes
964        .map_or(prp_capacity_bytes, |max_transfer| {
965            prp_capacity_bytes.min(max_transfer)
966        })
967        .max(lba_size);
968    let max_blocks = max_bytes
969        .checked_div(lba_size)
970        .unwrap_or(1)
971        .max(1)
972        .min(u16::MAX as usize + 1) as u32;
973    let max_bytes = (max_blocks as usize).saturating_mul(lba_size);
974    QueueLimits {
975        dma_mask,
976        dma_domain: dma_api::DmaDomainId::legacy_global(),
977        dma_alignment,
978        max_inflight: max_inflight.max(1),
979        max_blocks_per_request: max_blocks,
980        max_segments: prp_entries + 1,
981        max_segment_size: max_bytes,
982        supported_flags: RequestFlags::NONE,
983        // Do not advertise flush until the driver plumbs a reliable capability
984        // check from Identify/Feature data. Some QEMU NVMe backends reject the
985        // Flush command with "Invalid Field", which must not surface as fsync
986        // I/O errors.
987        supports_flush: false,
988        supports_discard: false,
989        supports_write_zeroes: false,
990    }
991}
992
993#[cfg(test)]
994mod tests {
995    use super::{
996        CachedCompletion, CompletionCache, CompletionStatus, PrpPageAccumulator, RequestSlot,
997        SlotState, irq_sources_from_queue_bits, limits, source_queue_bits,
998    };
999    use crate::Namespace;
1000
1001    #[test]
1002    fn queue_limits_align_dma_to_nvme_page_size() {
1003        let namespace = Namespace {
1004            id: 1,
1005            lba_size: 512,
1006            lba_count: 1024,
1007            metadata_size: 0,
1008        };
1009        let limits = limits(u64::MAX, 4096, None, namespace, 8);
1010
1011        assert_eq!(limits.dma_alignment, 4096);
1012        assert_eq!(limits.max_segments, 513);
1013        assert_eq!(limits.max_segment_size, 4096 * 513);
1014        assert!(limits.max_blocks_per_request >= 8);
1015        assert!(!limits.supports_flush);
1016    }
1017
1018    #[test]
1019    fn queue_limits_keep_prp_capacity_tied_to_controller_page() {
1020        let namespace = Namespace {
1021            id: 1,
1022            lba_size: 8192,
1023            lba_count: 1024,
1024            metadata_size: 0,
1025        };
1026        let limits = limits(u64::MAX, 4096, None, namespace, 8);
1027
1028        assert_eq!(limits.dma_alignment, 8192);
1029        assert_eq!(limits.max_segments, 513);
1030        assert_eq!(limits.max_segment_size, 8192 * 256);
1031        assert_eq!(limits.max_blocks_per_request, 256);
1032    }
1033
1034    #[test]
1035    fn queue_limits_respect_controller_transfer_limit() {
1036        let namespace = Namespace {
1037            id: 1,
1038            lba_size: 512,
1039            lba_count: 1024,
1040            metadata_size: 0,
1041        };
1042        let limits = limits(u64::MAX, 4096, Some(512 * 1024), namespace, 8);
1043
1044        assert_eq!(limits.max_blocks_per_request, 1024);
1045        assert_eq!(limits.max_segment_size, 512 * 1024);
1046    }
1047
1048    #[test]
1049    fn legacy_irq_source_covers_all_created_queues() {
1050        let sources = irq_sources_from_queue_bits(false, &[], 0b1011);
1051
1052        assert_eq!(sources.len(), 1);
1053        assert_eq!(sources[0].id, 0);
1054        assert_eq!(sources[0].queues.bits(), 0b1011);
1055        assert_eq!(source_queue_bits(false, &[], 0, 0b1011), 0b1011);
1056        assert_eq!(source_queue_bits(false, &[], 1, 0b1011), 0);
1057    }
1058
1059    #[test]
1060    fn msix_irq_sources_group_queues_by_vector() {
1061        let vectors = [4, 5, 4];
1062        let sources = irq_sources_from_queue_bits(true, &vectors, 0b111);
1063
1064        assert_eq!(sources.len(), 2);
1065        assert_eq!(sources[0].id, 4);
1066        assert_eq!(sources[0].queues.bits(), 0b101);
1067        assert_eq!(sources[1].id, 5);
1068        assert_eq!(sources[1].queues.bits(), 0b010);
1069        assert_eq!(source_queue_bits(true, &vectors, 4, 0b111), 0b101);
1070    }
1071
1072    #[test]
1073    fn prp_pages_split_at_controller_page_boundaries() {
1074        let mut pages = PrpPageAccumulator::new();
1075
1076        pages.push_segment(0x1800, 4096, 4096).unwrap();
1077
1078        assert_eq!(pages.into_pages(), [0x1800, 0x2000]);
1079    }
1080
1081    #[test]
1082    fn prp_pages_coalesce_contiguous_split_segments() {
1083        let mut pages = PrpPageAccumulator::new();
1084
1085        pages.push_segment(0x1000, 4096, 4096).unwrap();
1086        pages.push_segment(0x2000, 2048, 4096).unwrap();
1087        pages.push_segment(0x2800, 2048, 4096).unwrap();
1088
1089        assert_eq!(pages.into_pages(), [0x1000, 0x2000]);
1090    }
1091
1092    #[test]
1093    fn prp_pages_reject_unaligned_non_contiguous_segment() {
1094        let mut pages = PrpPageAccumulator::new();
1095
1096        pages.push_segment(0x1000, 2048, 4096).unwrap();
1097
1098        assert!(pages.push_segment(0x2800, 512, 4096).is_err());
1099    }
1100
1101    #[test]
1102    fn cached_completion_does_not_complete_slot_until_task_consumes_it() {
1103        let cache = CompletionCache::new(4);
1104        let mut slots = test_slots(4);
1105        slots[2].state = SlotState::Pending;
1106
1107        cache.extend(alloc::vec![CachedCompletion::success(2)]);
1108
1109        assert_eq!(slots[2].state, SlotState::Pending);
1110        assert_eq!(cache.drain_into_slots(0, &mut slots), 1);
1111        assert_eq!(slots[2].state, SlotState::Complete);
1112    }
1113
1114    #[test]
1115    fn cached_failed_completion_marks_slot_failed_in_task_context() {
1116        let cache = CompletionCache::new(4);
1117        let mut slots = test_slots(4);
1118        slots[3].state = SlotState::Pending;
1119
1120        cache.extend(alloc::vec![CachedCompletion::failed(3, 0x4002)]);
1121
1122        assert_eq!(cache.drain_into_slots(0, &mut slots), 1);
1123        assert_eq!(slots[3].state, SlotState::Failed);
1124    }
1125
1126    #[test]
1127    fn cached_completion_is_consumed_once() {
1128        let cache = CompletionCache::new(2);
1129        let mut slots = test_slots(2);
1130        slots[1].state = SlotState::Pending;
1131
1132        cache.extend(alloc::vec![CachedCompletion::success(1)]);
1133
1134        assert_eq!(cache.drain_into_slots(0, &mut slots), 1);
1135        assert_eq!(cache.drain_into_slots(0, &mut slots), 0);
1136        assert_eq!(slots[1].state, SlotState::Complete);
1137    }
1138
1139    fn test_slots(count: usize) -> alloc::vec::Vec<RequestSlot> {
1140        (0..count)
1141            .map(|_| RequestSlot {
1142                state: SlotState::Free,
1143                prp_list: None,
1144            })
1145            .collect()
1146    }
1147
1148    impl CachedCompletion {
1149        const fn success(cid: usize) -> Self {
1150            Self {
1151                cid,
1152                status: CompletionStatus {
1153                    success: true,
1154                    raw_status: 0,
1155                    result: 0,
1156                },
1157            }
1158        }
1159
1160        const fn failed(cid: usize, raw_status: u16) -> Self {
1161            Self {
1162                cid,
1163                status: CompletionStatus {
1164                    success: false,
1165                    raw_status,
1166                    result: 0,
1167                },
1168            }
1169        }
1170    }
1171}