Skip to main content

nvme_driver/
nvme.rs

1use alloc::vec::Vec;
2use core::ptr::NonNull;
3
4use dma_api::{CoherentArray, DeviceDma, DmaDirection, DmaOp};
5use log::{debug, info};
6use mmio_api::{Mmio, MmioAddr, MmioOp};
7
8use crate::{
9    command::{
10        self, ControllerInfo, Feature, Identify, IdentifyActiveNamespaceList, IdentifyController,
11        IdentifyNamespaceDataStructure,
12    },
13    err::*,
14    queue::{CommandSet, NvmeQueue},
15    registers::NvmeReg,
16};
17
18pub struct Nvme {
19    bar: NonNull<NvmeReg>,
20    _mmio: Option<Mmio>,
21    dma: DeviceDma,
22    admin_queue: NvmeQueue,
23    io_queues: Vec<Option<NvmeQueue>>,
24    num_ns: usize,
25    sqes: u32,
26    cqes: u32,
27    page_size: usize,
28    max_transfer_bytes: Option<usize>,
29    io_queue_interrupts: bool,
30    interrupt_vector: u32,
31}
32
33#[derive(Debug, Clone, Copy)]
34pub struct Config {
35    pub page_size: usize,
36    pub io_queue_pair_count: usize,
37    pub io_queue_interrupts: bool,
38    pub interrupt_vector: u32,
39}
40
41impl Config {
42    pub const fn new(page_size: usize, io_queue_pair_count: usize) -> Self {
43        Self {
44            page_size,
45            io_queue_pair_count,
46            io_queue_interrupts: false,
47            interrupt_vector: 0,
48        }
49    }
50
51    pub const fn with_intx_irq(mut self) -> Self {
52        self.io_queue_interrupts = true;
53        self.interrupt_vector = 0;
54        self
55    }
56}
57
58impl Nvme {
59    pub fn new(
60        bar_addr: impl Into<MmioAddr>,
61        bar_size: usize,
62        dma_mask: u64,
63        dma_op: &'static dyn DmaOp,
64        mmio_op: &'static dyn MmioOp,
65        config: Config,
66    ) -> Result<Self> {
67        mmio_api::init(mmio_op);
68        let mmio = mmio_api::ioremap(bar_addr.into(), bar_size)?;
69        let dma = DeviceDma::new_legacy(dma_mask, dma_op);
70        Self::new_mmio(mmio, dma, config)
71    }
72
73    fn new_mmio(mmio: Mmio, dma: DeviceDma, config: Config) -> Result<Self> {
74        let bar = NonNull::new(mmio.as_ptr()).expect("mmio mapping must not be null");
75        Self::new_with_bar(bar.cast(), Some(mmio), dma, config)
76    }
77
78    fn new_with_bar(
79        bar: NonNull<NvmeReg>,
80        mmio: Option<Mmio>,
81        dma: DeviceDma,
82        config: Config,
83    ) -> Result<Self> {
84        let admin_queue = NvmeQueue::new(0, bar, &dma, config.page_size, 64, 64)?;
85
86        assert!(config.io_queue_pair_count > 0);
87
88        let mut s = Self {
89            bar,
90            _mmio: mmio,
91            dma,
92            admin_queue,
93            io_queues: Vec::new(),
94            num_ns: 0,
95            sqes: 6,
96            cqes: 4,
97            page_size: config.page_size,
98            max_transfer_bytes: None,
99            io_queue_interrupts: config.io_queue_interrupts,
100            interrupt_vector: config.interrupt_vector,
101        };
102
103        let version = s.version();
104
105        info!(
106            "NVME @{bar:?} init begin, version: {}.{}.{} ",
107            version.0, version.1, version.2
108        );
109
110        s.init(config)?;
111
112        Ok(s)
113    }
114
115    pub fn dma_mask(&self) -> u64 {
116        self.dma.dma_mask()
117    }
118
119    fn reset(&mut self) {
120        self.reg().reset();
121    }
122
123    fn reset_and_setup_controller_info(&mut self) -> Result<ControllerInfo> {
124        self.reset();
125        self.nvme_configure_admin_queue();
126        self.reg().ready_for_read_controller_info();
127
128        self.get_identfy(IdentifyController::new())
129    }
130
131    fn init(&mut self, config: Config) -> Result {
132        let controller = self.reset_and_setup_controller_info()?;
133
134        debug!("Controller: {:?}", controller);
135
136        self.sqes = controller.sqes_min as _;
137        self.cqes = controller.cqes_min as _;
138        self.reset();
139        self.nvme_configure_admin_queue();
140        self.reg().setup_cc(self.sqes, self.cqes);
141        let controller = self.get_identfy(IdentifyController::new())?;
142
143        debug!("Controller: {:?}", controller);
144
145        self.num_ns = controller.number_of_namespaces as _;
146        self.max_transfer_bytes = controller_max_transfer_bytes(config.page_size, controller.mdts);
147        if config.io_queue_interrupts {
148            self.mask_interrupt_vector(config.interrupt_vector);
149        }
150        self.config_io_queue(config)?;
151
152        debug!("IO queue ok.");
153        loop {
154            let ns = self.get_identfy(IdentifyNamespaceDataStructure::new(1))?;
155            if let Some(ns) = ns {
156                debug!("Namespace: {:?}", ns);
157                break;
158            }
159        }
160        debug!("Namespace ok.");
161        Ok(())
162    }
163
164    pub fn namespace_list(&mut self) -> Result<Vec<Namespace>> {
165        let id_list = self.get_identfy(IdentifyActiveNamespaceList::new())?;
166        let mut out = Vec::new();
167
168        for id in id_list {
169            let ns = self
170                .get_identfy(IdentifyNamespaceDataStructure::new(id))?
171                .unwrap();
172
173            out.push(Namespace {
174                id,
175                lba_size: ns.lba_size as _,
176                lba_count: ns.namespace_size as _,
177                metadata_size: ns.metadata_size as _,
178            });
179        }
180
181        Ok(out)
182    }
183
184    // config admin queue
185    // 1. set admin queue(cq && sq) size
186    // 2. set admin queue(cq && sq) dma address
187    // 3. enable ctrl
188    fn nvme_configure_admin_queue(&mut self) {
189        self.reg().set_admin_submission_and_completion_queue_size(
190            self.admin_queue.sq_len(),
191            self.admin_queue.cq_len(),
192        );
193
194        self.reg()
195            .set_admin_submission_queue_base_address(self.admin_queue.sq_bus_addr());
196
197        self.reg()
198            .set_admin_completion_queue_base_address(self.admin_queue.cq_bus_addr());
199    }
200
201    fn config_io_queue(&mut self, config: Config) -> Result {
202        let num = config.io_queue_pair_count;
203        // 设置 io queue 数量
204        let cmd = CommandSet::set_features(Feature::NumberOfQueues {
205            nsq: num as u32 - 1,
206            ncq: num as u32 - 1,
207        });
208        self.admin_queue.command_sync(cmd)?;
209
210        for i in 0..num {
211            let id = (i + 1) as u32;
212            let io_queue = NvmeQueue::new(
213                id,
214                self.bar,
215                &self.dma,
216                config.page_size,
217                2usize.pow(self.sqes as _),
218                2usize.pow(self.cqes as _),
219            )?;
220
221            let data = CommandSet::create_io_completion_queue(
222                io_queue.qid,
223                io_queue.cq_len() as _,
224                io_queue.cq_bus_addr(),
225                true,
226                config.io_queue_interrupts,
227                config.interrupt_vector,
228            );
229            self.admin_queue.command_sync(data)?;
230
231            let data = CommandSet::create_io_submission_queue(
232                io_queue.qid,
233                io_queue.sq_len() as _,
234                io_queue.sq_bus_addr(),
235                true,
236                0,
237                io_queue.qid,
238                0,
239            );
240
241            self.admin_queue.command_sync(data)?;
242
243            self.io_queues.push(Some(io_queue));
244        }
245
246        Ok(())
247    }
248
249    pub fn io_queue_count(&self) -> usize {
250        self.io_queues.len()
251    }
252
253    pub fn page_size(&self) -> usize {
254        self.page_size
255    }
256
257    pub(crate) const fn max_transfer_bytes(&self) -> Option<usize> {
258        self.max_transfer_bytes
259    }
260
261    pub fn io_queue_interrupts_enabled(&self) -> bool {
262        self.io_queue_interrupts
263    }
264
265    pub fn interrupt_vector(&self) -> u32 {
266        self.interrupt_vector
267    }
268
269    pub fn mask_interrupt_vector(&mut self, vector: u32) {
270        self.reg().mask_interrupt_vector(vector);
271    }
272
273    pub fn unmask_interrupt_vector(&mut self, vector: u32) {
274        self.reg().unmask_interrupt_vector(vector);
275    }
276
277    pub(crate) fn take_io_queue(&mut self, index: usize) -> Option<NvmeQueue> {
278        self.io_queues.get_mut(index)?.take()
279    }
280
281    pub(crate) fn alloc_prp_list(&self) -> Result<CoherentArray<u64>> {
282        self.dma
283            .coherent_array_zero_with_align(
284                self.page_size / core::mem::size_of::<u64>(),
285                self.page_size,
286            )
287            .map_err(Into::into)
288    }
289
290    pub fn get_identfy<T: Identify>(&mut self, mut want: T) -> Result<T::Output> {
291        let cmd = want.command_set_mut();
292
293        cmd.cdw0 = CommandSet::cdw0_from_opcode(command::Opcode::IDENTIFY);
294        cmd.cdw10 = T::CNS;
295
296        let buff = self.dma.contiguous_array_zero_with_align::<u8>(
297            0x1000,
298            0x1000,
299            DmaDirection::FromDevice,
300        )?;
301        cmd.prp1 = buff.dma_addr().as_u64();
302
303        self.admin_queue.command_sync(*cmd)?;
304
305        let data = buff.read_from_device(buff.len(), |data| data.to_vec());
306        let res = want.parse(&data);
307        Ok(res)
308    }
309
310    pub fn block_write_sync(
311        &mut self,
312        ns: &Namespace,
313        block_start: u64,
314        buff: &[u8],
315    ) -> Result<()> {
316        assert!(
317            buff.len().is_multiple_of(ns.lba_size),
318            "buffer size must be multiple of lba size"
319        );
320
321        let mut dma_buff = self.dma.contiguous_array_zero_with_align::<u8>(
322            buff.len(),
323            ns.lba_size,
324            DmaDirection::ToDevice,
325        )?;
326        dma_buff.copy_to_device_from_slice(buff);
327
328        let blk_num = dma_buff.len() / ns.lba_size;
329
330        let cmd = CommandSet::nvm_cmd_write(
331            ns.id,
332            dma_buff.dma_addr().as_u64(),
333            block_start,
334            blk_num as _,
335        );
336
337        self.io_queues
338            .get_mut(0)
339            .and_then(Option::as_mut)
340            .ok_or(Error::Unknown("missing IO queue"))?
341            .command_sync(cmd)?;
342
343        Ok(())
344    }
345
346    pub fn block_read_sync(
347        &mut self,
348        ns: &Namespace,
349        block_start: u64,
350        buff: &mut [u8],
351    ) -> Result<()> {
352        assert!(
353            buff.len().is_multiple_of(ns.lba_size),
354            "buffer size must be multiple of lba size"
355        );
356
357        let dma_buff = self.dma.contiguous_array_zero_with_align::<u8>(
358            buff.len(),
359            ns.lba_size,
360            DmaDirection::FromDevice,
361        )?;
362
363        let blk_num = dma_buff.len() / ns.lba_size;
364
365        let cmd = CommandSet::nvm_cmd_read(
366            ns.id,
367            dma_buff.dma_addr().as_u64(),
368            block_start,
369            blk_num as _,
370        );
371
372        self.io_queues
373            .get_mut(0)
374            .and_then(Option::as_mut)
375            .ok_or(Error::Unknown("missing IO queue"))?
376            .command_sync(cmd)?;
377        dma_buff.copy_from_device_to_slice(buff);
378        Ok(())
379    }
380
381    pub fn version(&self) -> (usize, usize, usize) {
382        self.reg().version()
383    }
384
385    fn reg(&self) -> &NvmeReg {
386        unsafe { self.bar.as_ref() }
387    }
388}
389
390unsafe impl Send for Nvme {}
391
392fn controller_max_transfer_bytes(page_size: usize, mdts: u8) -> Option<usize> {
393    if mdts == 0 {
394        None
395    } else {
396        Some(page_size.checked_shl(u32::from(mdts)).unwrap_or(usize::MAX))
397    }
398}
399
400#[derive(Debug, Clone, Copy)]
401pub struct Namespace {
402    pub id: u32,
403    pub lba_size: usize,
404    pub lba_count: usize,
405    pub metadata_size: usize,
406}
407
408#[cfg(test)]
409mod tests {
410    use super::{Config, controller_max_transfer_bytes};
411
412    #[test]
413    fn config_defaults_to_polling_and_can_enable_intx() {
414        let config = Config::new(4096, 1);
415        assert!(!config.io_queue_interrupts);
416        assert_eq!(config.interrupt_vector, 0);
417
418        let irq_config = config.with_intx_irq();
419        assert!(irq_config.io_queue_interrupts);
420        assert_eq!(irq_config.interrupt_vector, 0);
421    }
422
423    #[test]
424    fn controller_mdts_zero_means_unrestricted_transfer_size() {
425        assert_eq!(controller_max_transfer_bytes(4096, 0), None);
426    }
427
428    #[test]
429    fn controller_mdts_scales_with_controller_page_size() {
430        assert_eq!(controller_max_transfer_bytes(4096, 7), Some(512 * 1024));
431    }
432}