1use alloc::{boxed::Box, sync::Arc, vec, vec::Vec};
2use core::{
3 any::Any,
4 cell::UnsafeCell,
5 sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
6};
7
8use dma_api::CoherentArray;
9use rdif_block::{
10 BlkError, DeviceInfo, DriverGeneric, Event, IQueue, IdList, Interface, IrqHandler,
11 IrqSourceInfo, IrqSourceList, QueueInfo, QueueLimits, Request, RequestFlags, RequestId,
12 RequestOp, RequestStatus, validate_request,
13};
14
15use crate::{
16 Namespace, Nvme,
17 err::{Error as NvmeError, Result as NvmeResult},
18 queue::{CommandSet, NvmeQueue as HardwareQueue},
19};
20
21const MAX_PRP_LIST_PAGES: usize = 1;
22const DEFAULT_QUEUE_DEPTH: usize = 64;
23
24struct NvmeBlockInner {
25 nvme: Nvme,
26 namespace: Namespace,
27}
28
29pub struct NvmeBlockDriver {
30 name: &'static str,
31 inner: Arc<NvmeBlockOwner>,
32 queue_depth: usize,
33 irq_handler_taken: bool,
34}
35
36struct NvmeBlockOwner {
37 inner: UnsafeCell<NvmeBlockInner>,
38 next_queue_id: AtomicUsize,
39 irq_enabled: AtomicBool,
40 pending_irq: AtomicU64,
41 created_queues: AtomicU64,
42}
43
44impl NvmeBlockDriver {
45 pub fn from_nvme(mut nvme: Nvme) -> NvmeResult<Self> {
46 let namespace = nvme
47 .namespace_list()?
48 .into_iter()
49 .next()
50 .ok_or(NvmeError::Unknown("no active namespace found"))?;
51
52 Ok(Self::with_namespace("nvme", nvme, namespace))
53 }
54
55 pub fn from_nvme_with_queue_depth(mut nvme: Nvme, queue_depth: usize) -> NvmeResult<Self> {
56 let namespace = nvme
57 .namespace_list()?
58 .into_iter()
59 .next()
60 .ok_or(NvmeError::Unknown("no active namespace found"))?;
61
62 Ok(Self::with_namespace_and_queue_depth(
63 "nvme",
64 nvme,
65 namespace,
66 queue_depth,
67 ))
68 }
69
70 pub fn with_namespace(name: &'static str, nvme: Nvme, namespace: Namespace) -> Self {
71 Self::with_namespace_and_queue_depth(name, nvme, namespace, DEFAULT_QUEUE_DEPTH)
72 }
73
74 pub fn with_namespace_and_queue_depth(
75 name: &'static str,
76 nvme: Nvme,
77 namespace: Namespace,
78 queue_depth: usize,
79 ) -> Self {
80 Self {
81 name,
82 inner: Arc::new(NvmeBlockOwner {
83 inner: UnsafeCell::new(NvmeBlockInner { nvme, namespace }),
84 next_queue_id: AtomicUsize::new(0),
85 irq_enabled: AtomicBool::new(true),
86 pending_irq: AtomicU64::new(0),
87 created_queues: AtomicU64::new(0),
88 }),
89 queue_depth: queue_depth.max(1),
90 irq_handler_taken: false,
91 }
92 }
93
94 pub fn namespace(&self) -> Namespace {
95 self.inner.with_mut(|inner| inner.namespace)
96 }
97
98 pub fn into_interface(self) -> Self {
99 self
100 }
101
102 fn device_info_for(&self) -> DeviceInfo {
103 self.inner
104 .with_mut(|inner| device_info(self.name, inner.namespace))
105 }
106
107 fn limits_for(&self) -> QueueLimits {
108 self.inner.with_mut(|inner| {
109 limits(
110 inner.nvme.dma_mask(),
111 inner.nvme.page_size(),
112 inner.namespace,
113 )
114 })
115 }
116}
117
118unsafe impl Send for NvmeBlockOwner {}
123
124unsafe impl Sync for NvmeBlockOwner {}
127
128impl NvmeBlockOwner {
129 fn with_mut<R>(&self, f: impl FnOnce(&mut NvmeBlockInner) -> R) -> R {
130 let inner = unsafe { &mut *self.inner.get() };
131 f(inner)
132 }
133}
134
135impl DriverGeneric for NvmeBlockDriver {
136 fn name(&self) -> &str {
137 self.name
138 }
139
140 fn raw_any(&self) -> Option<&dyn Any> {
141 Some(self)
142 }
143
144 fn raw_any_mut(&mut self) -> Option<&mut dyn Any> {
145 Some(self)
146 }
147}
148
149impl Interface for NvmeBlockDriver {
150 fn device_info(&self) -> DeviceInfo {
151 self.device_info_for()
152 }
153
154 fn queue_limits(&self) -> QueueLimits {
155 self.limits_for()
156 }
157
158 fn create_queue(&mut self) -> Option<Box<dyn IQueue>> {
159 let id = self.inner.next_queue_id.fetch_add(1, Ordering::Relaxed);
160 if id >= u64::BITS as usize {
161 return None;
162 }
163
164 let queue = self.inner.with_mut(|inner| {
165 let queue = inner.nvme.take_io_queue(id)?;
166 let depth = self.queue_depth.min(queue.depth().saturating_sub(1).max(1));
167 let prp_lists = alloc_prp_lists(&inner.nvme, depth).ok()?;
168 Some(NvmeBlockQueue::new(
169 id,
170 depth,
171 self.name,
172 inner.namespace,
173 inner.nvme.dma_mask(),
174 inner.nvme.page_size(),
175 queue,
176 prp_lists,
177 Arc::clone(&self.inner),
178 ))
179 })?;
180
181 self.inner
182 .created_queues
183 .fetch_or(1 << id, Ordering::AcqRel);
184 Some(Box::new(queue))
185 }
186
187 fn enable_irq(&self) {
188 self.inner.irq_enabled.store(true, Ordering::Release);
189 }
190
191 fn disable_irq(&self) {
192 self.inner.irq_enabled.store(false, Ordering::Release);
193 }
194
195 fn is_irq_enabled(&self) -> bool {
196 self.inner.irq_enabled.load(Ordering::Acquire)
197 }
198
199 fn irq_sources(&self) -> IrqSourceList {
200 let queues = IdList::from_bits(self.inner.created_queues.load(Ordering::Acquire));
201 vec![IrqSourceInfo::legacy(queues)]
202 }
203
204 fn take_irq_handler(&mut self, source_id: usize) -> Option<Box<dyn IrqHandler>> {
205 if source_id != 0 || self.irq_handler_taken {
206 return None;
207 }
208 self.irq_handler_taken = true;
209 Some(Box::new(NvmeIrqHandler {
210 inner: Arc::clone(&self.inner),
211 }))
212 }
213}
214
215struct NvmeIrqHandler {
216 inner: Arc<NvmeBlockOwner>,
217}
218
219impl IrqHandler for NvmeIrqHandler {
220 fn handle_irq(&self) -> Event {
221 if !self.inner.irq_enabled.load(Ordering::Acquire) {
222 return Event::none();
223 }
224 let pending = self.inner.pending_irq.swap(0, Ordering::AcqRel);
225 if pending == 0 {
226 Event::from_queue_bits(self.inner.created_queues.load(Ordering::Acquire))
227 } else {
228 Event::from_queue_bits(pending)
229 }
230 }
231}
232
233struct NvmeBlockQueue {
234 id: usize,
235 name: &'static str,
236 namespace: Namespace,
237 dma_mask: u64,
238 page_size: usize,
239 queue: HardwareQueue,
240 slots: Vec<RequestSlot>,
241 free_cids: Vec<usize>,
242 free_prp_lists: Vec<CoherentArray<u64>>,
243 owner: Arc<NvmeBlockOwner>,
244}
245
246struct RequestSlot {
247 state: SlotState,
248 prp_list: Option<CoherentArray<u64>>,
249}
250
251#[derive(Clone, Copy, Debug, PartialEq, Eq)]
252enum SlotState {
253 Free,
254 Pending,
255 Complete,
256 Failed,
257}
258
259struct PrpMapping {
260 prp1: u64,
261 prp2: u64,
262 prp_list: Option<CoherentArray<u64>>,
263}
264
265impl NvmeBlockQueue {
266 #[allow(clippy::too_many_arguments)]
267 fn new(
268 id: usize,
269 depth: usize,
270 name: &'static str,
271 namespace: Namespace,
272 dma_mask: u64,
273 page_size: usize,
274 queue: HardwareQueue,
275 prp_lists: Vec<CoherentArray<u64>>,
276 owner: Arc<NvmeBlockOwner>,
277 ) -> Self {
278 let mut slots = Vec::with_capacity(depth + 1);
279 slots.resize_with(depth + 1, || RequestSlot {
280 state: SlotState::Free,
281 prp_list: None,
282 });
283 let free_cids = (1..=depth).rev().collect();
284
285 Self {
286 id,
287 name,
288 namespace,
289 dma_mask,
290 page_size,
291 queue,
292 slots,
293 free_cids,
294 free_prp_lists: prp_lists,
295 owner,
296 }
297 }
298
299 fn queue_info(&self) -> QueueInfo {
300 QueueInfo {
301 id: self.id,
302 device: device_info(self.name, self.namespace),
303 limits: limits(self.dma_mask, self.page_size, self.namespace),
304 }
305 }
306
307 fn alloc_cid(&mut self) -> Result<usize, BlkError> {
308 self.free_cids.pop().ok_or(BlkError::Retry)
309 }
310
311 fn free_cid(&mut self, cid: usize) {
312 if cid < self.slots.len() {
313 if let Some(prp_list) = self.slots[cid].prp_list.take() {
314 self.free_prp_lists.push(prp_list);
315 }
316 self.slots[cid].state = SlotState::Free;
317 self.free_cids.push(cid);
318 }
319 }
320
321 fn build_command(&mut self, cid: usize, request: &Request<'_>) -> Result<CommandSet, BlkError> {
322 let cid = u16::try_from(cid).map_err(|_| BlkError::InvalidRequest)?;
323 match request.op {
324 RequestOp::Read | RequestOp::Write => {
325 let prp = self.build_prp_mapping(request)?;
326 let command = match request.op {
327 RequestOp::Read => CommandSet::nvm_cmd_read_with_cid(
328 self.namespace.id,
329 prp.prp1,
330 prp.prp2,
331 request.lba,
332 request.block_count,
333 cid,
334 ),
335 RequestOp::Write => CommandSet::nvm_cmd_write_with_cid(
336 self.namespace.id,
337 prp.prp1,
338 prp.prp2,
339 request.lba,
340 request.block_count,
341 cid,
342 ),
343 _ => unreachable!(),
344 };
345 self.slots[usize::from(cid)].prp_list = prp.prp_list;
346 Ok(command)
347 }
348 RequestOp::Flush => Ok(CommandSet::nvm_cmd_flush_with_cid(self.namespace.id, cid)),
349 RequestOp::Discard | RequestOp::WriteZeroes => Err(BlkError::NotSupported),
350 }
351 }
352
353 fn build_prp_mapping(&mut self, request: &Request<'_>) -> Result<PrpMapping, BlkError> {
354 let mut pages = Vec::new();
355 for segment in request.segments.iter() {
356 push_prp_pages(&mut pages, segment.bus, segment.len, self.page_size)?;
357 }
358 let prp1 = *pages.first().ok_or(BlkError::InvalidRequest)?;
359 let prp2 = match pages.len() {
360 1 => 0,
361 2 => pages[1],
362 _ => {
363 let list_entries = self.page_size / core::mem::size_of::<u64>();
364 if pages.len() - 1 > list_entries * MAX_PRP_LIST_PAGES {
365 return Err(BlkError::InvalidRequest);
366 }
367 let mut list = self.free_prp_lists.pop().ok_or(BlkError::Retry)?;
368 for entry in 0..list_entries {
369 list.set_cpu(entry, 0);
370 }
371 for (entry, addr) in pages[1..].iter().copied().enumerate() {
372 list.set_cpu(entry, addr);
373 }
374 let addr = list.dma_addr().as_u64();
375 return Ok(PrpMapping {
376 prp1,
377 prp2: addr,
378 prp_list: Some(list),
379 });
380 }
381 };
382 Ok(PrpMapping {
383 prp1,
384 prp2,
385 prp_list: None,
386 })
387 }
388
389 fn drain_completions(&mut self) {
390 while let Some(completion) = self.queue.poll_completion() {
391 let cid = usize::from(completion.command_id);
392 if let Some(slot) = self.slots.get_mut(cid) {
393 slot.state = if completion.status.is_success() {
394 SlotState::Complete
395 } else {
396 SlotState::Failed
397 };
398 }
399 }
400 }
401
402 fn insert_pending_irq(&self) {
403 if self.owner.irq_enabled.load(Ordering::Acquire) && self.id < u64::BITS as usize {
404 self.owner
405 .pending_irq
406 .fetch_or(1 << self.id, Ordering::AcqRel);
407 }
408 }
409}
410
411unsafe impl IQueue for NvmeBlockQueue {
415 fn id(&self) -> usize {
416 self.id
417 }
418
419 fn info(&self) -> QueueInfo {
420 self.queue_info()
421 }
422
423 fn submit_request(&mut self, request: Request<'_>) -> Result<RequestId, BlkError> {
424 let info = self.queue_info();
425 validate_request(info, &request)?;
426 self.drain_completions();
427
428 let cid = self.alloc_cid()?;
429 let command = match self.build_command(cid, &request) {
430 Ok(command) => command,
431 Err(err) => {
432 self.free_cid(cid);
433 return Err(err);
434 }
435 };
436 self.slots[cid].state = SlotState::Pending;
437 self.queue.submit_io_data(command);
438 self.insert_pending_irq();
439 Ok(RequestId::new(cid))
440 }
441
442 fn poll_request(&mut self, request: RequestId) -> Result<RequestStatus, BlkError> {
443 self.drain_completions();
444
445 let cid = usize::from(request);
446 match self.slots.get(cid).map(|slot| slot.state) {
447 Some(SlotState::Pending) => Ok(RequestStatus::Pending),
448 Some(SlotState::Complete) => {
449 self.free_cid(cid);
450 Ok(RequestStatus::Complete)
451 }
452 Some(SlotState::Failed) => {
453 self.free_cid(cid);
454 Err(BlkError::Io)
455 }
456 Some(SlotState::Free) | None => Err(BlkError::InvalidRequest),
457 }
458 }
459}
460
461fn alloc_prp_lists(nvme: &Nvme, depth: usize) -> NvmeResult<Vec<CoherentArray<u64>>> {
462 let mut lists = Vec::with_capacity(depth);
463 for _ in 0..depth {
464 lists.push(nvme.alloc_prp_list()?);
465 }
466 Ok(lists)
467}
468
469fn push_prp_pages(
470 pages: &mut Vec<u64>,
471 mut addr: u64,
472 mut len: usize,
473 page_size: usize,
474) -> Result<(), BlkError> {
475 if page_size == 0 || len == 0 {
476 return Err(BlkError::InvalidRequest);
477 }
478
479 while len > 0 {
480 pages.push(addr);
481 let offset = addr as usize % page_size;
482 let chunk = page_size.saturating_sub(offset).min(len);
483 if chunk == 0 {
484 return Err(BlkError::InvalidRequest);
485 }
486 addr = addr
487 .checked_add(chunk as u64)
488 .ok_or(BlkError::InvalidRequest)?;
489 len -= chunk;
490 }
491 Ok(())
492}
493
494fn device_info(name: &'static str, namespace: Namespace) -> DeviceInfo {
495 DeviceInfo {
496 name: Some(name),
497 model: Some("nvme"),
498 ..DeviceInfo::new(namespace.lba_count as u64, namespace.lba_size)
499 }
500}
501
502fn limits(dma_mask: u64, page_size: usize, namespace: Namespace) -> QueueLimits {
503 let prp_entries = page_size / core::mem::size_of::<u64>();
504 let max_bytes = page_size.saturating_mul(prp_entries + 1);
505 let max_blocks = max_bytes
506 .checked_div(namespace.lba_size.max(1))
507 .unwrap_or(1)
508 .max(1)
509 .min(u16::MAX as usize + 1) as u32;
510 QueueLimits {
511 dma_mask,
512 dma_alignment: namespace.lba_size.max(1),
513 max_blocks_per_request: max_blocks,
514 max_segments: prp_entries + 1,
515 max_segment_size: max_bytes,
516 supported_flags: RequestFlags::NONE,
517 supports_flush: true,
518 supports_discard: false,
519 supports_write_zeroes: false,
520 }
521}