Read real block sizes.
This commit is contained in:
+50
-7
@@ -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();
|
||||
|
||||
|
||||
+131
-7
@@ -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<std::cmp::Ordering> {
|
||||
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<u64> {
|
||||
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<IdentifyNamespaceData> = 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-24
@@ -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<Option<usize>> {
|
||||
//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<Option<usize>> {
|
||||
//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;
|
||||
}
|
||||
|
||||
+6
-8
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user