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
131unsafe impl Send for NvmeBlockOwner {}
135
136unsafe 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
568unsafe impl Send for NvmeQueueCore {}
573
574unsafe 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
767unsafe 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 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}