From 2db3bf468915a2a8cd736f75ae87e3e696ab7ba4 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 1 May 2020 20:00:24 +0200 Subject: [PATCH] Read real block sizes. --- nvmed/src/main.rs | 57 +++++++++++++-- nvmed/src/nvme/identify.rs | 138 +++++++++++++++++++++++++++++++++++-- nvmed/src/nvme/mod.rs | 49 ++++++------- nvmed/src/scheme.rs | 14 ++-- 4 files changed, 212 insertions(+), 46 deletions(-) diff --git a/nvmed/src/main.rs b/nvmed/src/main.rs index 483c9aafe5..1baf366ad6 100644 --- a/nvmed/src/main.rs +++ b/nvmed/src/main.rs @@ -1,4 +1,3 @@ -use std::collections::BTreeMap; use std::convert::TryInto; use std::fs::File; use std::io::{ErrorKind, Read, Write}; @@ -9,12 +8,10 @@ use std::{slice, usize}; use pcid_interface::{PciBar, PciFeature, PciFeatureInfo, PciFunction, PcidServerHandle}; use syscall::{ - CloneFlags, Event, Mmio, Packet, Result, SchemeBlockMut, EVENT_READ, PHYSMAP_NO_CACHE, + CloneFlags, Event, Mmio, Packet, Result, SchemeBlockMut, PHYSMAP_NO_CACHE, PHYSMAP_WRITE, }; - -use arrayvec::ArrayVec; -use log::{debug, error, info, trace, warn}; +use redox_log::{OutputBuilder, RedoxLogger}; use self::nvme::{InterruptMethod, InterruptSources, Nvme}; use self::scheme::DiskScheme; @@ -190,7 +187,7 @@ fn get_int_method( message_data: Some(msg_data), multi_message_enable: Some(0), // enable 2^0=1 vectors mask_bits: None, - })); + })).unwrap(); (0, irq_handle) }; @@ -213,12 +210,58 @@ fn get_int_method( } } +fn setup_logging() -> Option<&'static RedoxLogger> { + let mut logger = RedoxLogger::new() + .with_output( + OutputBuilder::stderr() + .with_filter(log::LevelFilter::Info) // limit global output to important info + .with_ansi_escape_codes() + .flush_on_newline(true) + .build() + ); + + #[cfg(target_os = "redox")] + match OutputBuilder::in_redox_logging_scheme("disk", "pcie", "nvme.log") { + Ok(b) => logger = logger.with_output( + // TODO: Add a configuration file for this + b.with_filter(log::LevelFilter::Trace) + .flush_on_newline(true) + .build() + ), + Err(error) => eprintln!("nvmed: failed to create nvme.log: {}", error), + } + + #[cfg(target_os = "redox")] + match OutputBuilder::in_redox_logging_scheme("disk", "pcie", "nvme.ansi.log") { + Ok(b) => logger = logger.with_output( + b.with_filter(log::LevelFilter::Trace) + .with_ansi_escape_codes() + .flush_on_newline(true) + .build() + ), + Err(error) => eprintln!("nvmed: failed to create nvme.ansi.log: {}", error), + } + + match logger.enable() { + Ok(logger_ref) => { + eprintln!("nvmed: enabled logger"); + Some(logger_ref) + } + Err(error) => { + eprintln!("nvmed: failed to set default logger: {}", error); + None + } + } +} + fn main() { // Daemonize if unsafe { syscall::clone(CloneFlags::empty()).unwrap() } != 0 { return; } + let _logger_ref = setup_logging(); + let mut pcid_handle = PcidServerHandle::connect_default().expect("nvmed: failed to setup channel to pcid"); let pci_config = pcid_handle @@ -235,7 +278,7 @@ fn main() { let mut name = pci_config.func.name(); name.push_str("_nvme"); - info!("NVME PCI CONFIG: {:?}", pci_config); + log::info!("NVME PCI CONFIG: {:?}", pci_config); let allocated_bars = AllocatedBars::default(); diff --git a/nvmed/src/nvme/identify.rs b/nvmed/src/nvme/identify.rs index 4bcb830d22..98937ec72f 100644 --- a/nvmed/src/nvme/identify.rs +++ b/nvmed/src/nvme/identify.rs @@ -2,6 +2,7 @@ use syscall::Dma; use super::{Nvme, NvmeCmd, NvmeNamespace}; +/// See NVME spec section 5.15.2.2. #[derive(Clone, Copy)] #[repr(packed)] pub struct IdentifyControllerData { @@ -18,6 +19,129 @@ pub struct IdentifyControllerData { // TODO: Lots of fields pub _4k_pad: [u8; 4096 - 72], } + +/// See NVME spec section 5.15.2.1. +#[derive(Clone, Copy)] +#[repr(packed)] +pub struct IdentifyNamespaceData { + pub nsze: u64, + pub ncap: u64, + pub nuse: u64, + + pub nsfeat: u8, + pub nlbaf: u8, + pub flbas: u8, + pub mc: u8, + + pub dpc: u8, + pub dps: u8, + pub nmic: u8, + pub rescap: u8, + + pub fpi: u8, + pub dlfeat: u8, + pub nawun: u16, + + pub nawupf: u16, + pub nacwu: u16, + pub nabsn: u16, + pub nabo: u16, + + pub nabspf: u16, + pub noiob: u16, + + pub nvmcap: u64, + pub npwg: u16, + pub npwa: u16, + pub npdg: u16, + pub npda: u16, + + pub nows: u16, + pub _rsvd1: [u8; 18], + + pub anagrpid: u32, + pub _rsvd2: [u8; 3], + pub nsattr: u8, + + pub nvmsetid: u16, + pub endgid: u16, + pub nguid: [u8; 16], + pub eui64: u64, + + pub lba_format_support: [LbaFormat; 16], + pub _rsvd3: [u8; 192], + pub vendor_specific: [u8; 3712], +} + +impl IdentifyNamespaceData { + pub fn size_in_blocks(&self) -> u64 { + self.nsze + } + pub fn capacity_in_blocks(&self) -> u64 { + self.ncap + } + /// Guaranteed to be within 0..=15 + pub fn formatted_lba_size_idx(&self) -> usize { + (self.flbas & 0xF) as usize + } + pub fn formatted_lba_size(&self) -> &LbaFormat { + &self.lba_format_support[self.formatted_lba_size_idx()] + } + pub fn has_metadata_after_data(&self) -> bool { + (self.flbas & (1 << 4)) != 0 + } +} + +#[derive(Clone, Copy)] +#[repr(packed)] +pub struct LbaFormat(pub u32); + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum RelativePerformance { + Best = 0b00, + Better, + Good, + Degraded, +} +impl Ord for RelativePerformance { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + // higher performance is better, hence reversed + Ord::cmp(&(*self as u8), &(*other as u8)).reverse() + } +} +impl PartialOrd for RelativePerformance { + fn partial_cmp(&self, other: &Self) -> Option { + Some(Ord::cmp(self, other)) + } +} + +impl LbaFormat { + pub fn relative_performance(&self) -> RelativePerformance { + match ((self.0 >> 24) & 0b11) { + 0b00 => RelativePerformance::Best, + 0b01 => RelativePerformance::Better, + 0b10 => RelativePerformance::Good, + 0b11 => RelativePerformance::Degraded, + _ => unreachable!(), + } + } + pub fn is_available(&self) -> bool { + self.log_lba_data_size() != 0 + } + pub fn log_lba_data_size(&self) -> u8 { + ((self.0 >> 16) & 0xFF) as u8 + } + pub fn lba_data_size(&self) -> Option { + if self.log_lba_data_size() < 9 { return None } + if self.log_lba_data_size() >= 32 { return None } + Some(1u64 << self.log_lba_data_size()) + } + pub fn metadata_size(&self) -> u16 { + (self.0 & 0xFFFF) as u16 + } +} + impl Nvme { /// Returns the serial number, model, and firmware, in that order. pub async fn identify_controller(&self) { @@ -66,7 +190,7 @@ impl Nvme { } pub async fn identify_namespace(&self, nsid: u32) -> NvmeNamespace { //TODO: Use buffer - let data: Dma<[u8; 4096]> = Dma::zeroed().unwrap(); + let data: Dma = Dma::zeroed().unwrap(); // println!(" - Attempting to identify namespace {}", nsid); let cmd_id = self @@ -78,17 +202,17 @@ impl Nvme { // println!(" - Dumping identify namespace"); - // TODO: Use struct - let size = unsafe { *(data.as_ptr().offset(0) as *const u64) }; - let capacity = unsafe { *(data.as_ptr().offset(8) as *const u64) }; - println!(" - ID: {} Size: {} Capacity: {}", nsid, size, capacity); + let size = data.size_in_blocks(); + let capacity = data.capacity_in_blocks(); + log::info!("NSID: {} Size: {} Capacity: {}", nsid, size, capacity); - //TODO: Read block size + let block_size = data.formatted_lba_size().lba_data_size().expect("nvmed: error: size outside 512-2^64 range"); + log::debug!("NVME block size: {}", block_size); NvmeNamespace { id: nsid, blocks: size, - block_size: 512, // TODO + block_size, } } } diff --git a/nvmed/src/nvme/mod.rs b/nvmed/src/nvme/mod.rs index e2c194db96..96f86e1bd5 100644 --- a/nvmed/src/nvme/mod.rs +++ b/nvmed/src/nvme/mod.rs @@ -181,7 +181,7 @@ pub enum FullSqHandling { /// Return an error immediately prior to posting the command. ErrorDirectly, - /// Tell the IRQ reactor that we wan't to be notified when a command on the same submission + /// Tell the IRQ reactor that we want to be notified when a command on the same submission /// queue has been completed. Wait, } @@ -250,18 +250,21 @@ impl Nvme { buffer_prp[i] = (buffer.physical() + i * 4096) as u64; } - // println!(" - CAPS: {:X}", self.regs.cap.read()); - // println!(" - VS: {:X}", self.regs.vs.read()); - // println!(" - CC: {:X}", self.regs.cc.read()); - // println!(" - CSTS: {:X}", self.regs.csts.read()); + { + let regs = self.regs.read().unwrap(); + log::debug!("CAPS: {:X}", regs.cap.read()); + log::debug!("VS: {:X}", regs.vs.read()); + log::debug!("CC: {:X}", regs.cc.read()); + log::debug!("CSTS: {:X}", regs.csts.read()); + } - // println!(" - Disable"); + log::debug!("Disabling controller."); self.regs.get_mut().unwrap().cc.writef(1, false); - // println!(" - Waiting for not ready"); + log::trace!("Waiting for not ready."); loop { let csts = self.regs.get_mut().unwrap().csts.read(); - // println!("CSTS: {:X}", csts); + log::trace!("CSTS: {:X}", csts); if csts & 1 == 1 { std::arch::x86_64::_mm_pause(); } else { @@ -282,12 +285,12 @@ impl Nvme { for (qid, queue) in self.completion_queues.get_mut().unwrap().iter_mut() { let &(ref cq, ref sq_ids) = &*queue.get_mut().unwrap(); let data = &cq.data; - // println!(" - completion queue {}: {:X}, {}", qid, data.physical(), data.len()); + log::debug!("completion queue {}: {:X}, {}, (submission queue ids: {:?}", qid, data.physical(), data.len(), sq_ids); } for (qid, queue) in self.submission_queues.get_mut().unwrap().iter_mut() { let data = &queue.get_mut().unwrap().data; - // println!(" - submission queue {}: {:X}, {}", qid, data.physical(), data.len()); + log::debug!("submission queue {}: {:X}, {}", qid, data.physical(), data.len()); } { @@ -309,13 +312,13 @@ impl Nvme { regs.cc.write(cc); } - // println!(" - Enable"); + log::debug!("Enabling controller."); self.regs.get_mut().unwrap().cc.writef(1, true); - // println!(" - Waiting for ready"); + log::debug!("Waiting for ready"); loop { let csts = self.regs.get_mut().unwrap().csts.read(); - // println!("CSTS: {:X}", csts); + log::debug!("CSTS: {:X}", csts); if csts & 1 == 0 { std::arch::x86_64::_mm_pause(); } else { @@ -541,13 +544,13 @@ impl Nvme { async fn namespace_rw( &self, + namespace: &NvmeNamespace, nsid: u32, lba: u64, blocks_1: u16, write: bool, ) -> Result<()> { - //TODO: Get real block size - let block_size = 512; + let block_size = namespace.block_size; let buffer_prp_guard = self.buffer_prp.lock().unwrap(); @@ -576,14 +579,14 @@ impl Nvme { pub async fn namespace_read( &self, + namespace: &NvmeNamespace, nsid: u32, mut lba: u64, buf: &mut [u8], ) -> Result> { - //TODO: Get real block size - let block_size = 512; + let block_size = namespace.block_size as usize; - let mut buffer_guard = self.buffer.lock().unwrap(); + let buffer_guard = self.buffer.lock().unwrap(); for chunk in buf.chunks_mut(buffer_guard.len()) { let blocks = (chunk.len() + block_size - 1) / block_size; @@ -591,7 +594,7 @@ impl Nvme { assert!(blocks > 0); assert!(blocks <= 0x1_0000); - self.namespace_rw(nsid, lba, (blocks - 1) as u16, false).await?; + self.namespace_rw(namespace, nsid, lba, (blocks - 1) as u16, false).await?; chunk.copy_from_slice(&buffer_guard[..chunk.len()]); @@ -603,14 +606,12 @@ impl Nvme { pub async fn namespace_write( &self, + namespace: &NvmeNamespace, nsid: u32, mut lba: u64, buf: &[u8], ) -> Result> { - //TODO: Use interrupts - - //TODO: Get real block size - let block_size = 512; + let block_size = namespace.block_size as usize; let mut buffer_guard = self.buffer.lock().unwrap(); @@ -622,7 +623,7 @@ impl Nvme { buffer_guard[..chunk.len()].copy_from_slice(chunk); - self.namespace_rw(nsid, lba, (blocks - 1) as u16, true).await?; + self.namespace_rw(namespace, nsid, lba, (blocks - 1) as u16, true).await?; lba += blocks as u64; } diff --git a/nvmed/src/scheme.rs b/nvmed/src/scheme.rs index 090fdb67ba..aabec0389e 100644 --- a/nvmed/src/scheme.rs +++ b/nvmed/src/scheme.rs @@ -82,7 +82,7 @@ impl DiskWrapper { return Err(io::Error::from_raw_os_error(syscall::EOVERFLOW)); } loop { - match futures::executor::block_on(nvme.namespace_read(disk.id, block, block_bytes)) + match futures::executor::block_on(nvme.namespace_read(disk, disk.id, block, block_bytes)) .map_err(|err| io::Error::from_raw_os_error(err.errno))? { Some(bytes) => { assert_eq!(bytes, block_bytes.len()); @@ -347,7 +347,7 @@ impl SchemeBlockMut for DiskScheme { Handle::Disk(number, ref mut size) => { let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?; let block_size = disk.as_ref().block_size; - if let Some(count) = futures::executor::block_on(self.nvme.namespace_read(disk.as_ref().id, (*size as u64) / block_size, buf))? { + if let Some(count) = futures::executor::block_on(self.nvme.namespace_read(disk.as_ref(), disk.as_ref().id, (*size as u64) / block_size, buf))? { *size += count; Ok(Some(count)) } else { @@ -372,7 +372,7 @@ impl SchemeBlockMut for DiskScheme { let abs_block = part.start_lba + rel_block; - if let Some(count) = futures::executor::block_on(self.nvme.namespace_read(disk.as_ref().id, abs_block, buf))? { + if let Some(count) = futures::executor::block_on(self.nvme.namespace_read(disk.as_ref(), disk.as_ref().id, abs_block, buf))? { *offset += count; Ok(Some(count)) } else { @@ -388,10 +388,8 @@ impl SchemeBlockMut for DiskScheme { Handle::Disk(number, ref mut size) => { let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?; let block_size = disk.as_ref().block_size; - if let Some(count) = unsafe { - futures::executor::block_on(self.nvme - .namespace_write(disk.as_ref().id, (*size as u64) / block_size, buf))? - } { + if let Some(count) = futures::executor::block_on(self.nvme + .namespace_write(disk.as_ref(), disk.as_ref().id, (*size as u64) / block_size, buf))? { *size += count; Ok(Some(count)) } else { @@ -416,7 +414,7 @@ impl SchemeBlockMut for DiskScheme { let abs_block = part.start_lba + rel_block; - if let Some(count) = futures::executor::block_on(self.nvme.namespace_write(disk.as_ref().id, abs_block, buf))? { + if let Some(count) = futures::executor::block_on(self.nvme.namespace_write(disk.as_ref(), disk.as_ref().id, abs_block, buf))? { *offset += count; Ok(Some(count)) } else {