nvmed: add scheme with read/write implemented using polling

This commit is contained in:
Jeremy Soller
2019-08-24 20:24:01 -06:00
parent 9c277981be
commit b2c76034b7
3 changed files with 438 additions and 57 deletions
+72 -45
View File
@@ -1,4 +1,3 @@
//#![deny(warnings)]
#![feature(asm)]
extern crate bitflags;
@@ -6,25 +5,17 @@ extern crate spin;
extern crate syscall;
use std::{env, usize};
use std::fs::File;
use std::io::{ErrorKind, Read, Write};
use std::os::unix::io::{RawFd, FromRawFd};
use syscall::{EVENT_READ, PHYSMAP_NO_CACHE, PHYSMAP_WRITE, Event, Packet, Result, Scheme};
use syscall::{EVENT_READ, PHYSMAP_NO_CACHE, PHYSMAP_WRITE, Event, Packet, Result, SchemeBlockMut};
use self::nvme::Nvme;
use self::scheme::DiskScheme;
mod nvme;
/*
fn create_scheme_fallback<'a>(name: &'a str, fallback: &'a str) -> Result<(&'a str, RawFd)> {
if let Ok(fd) = syscall::open(&format!(":{}", name), syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK) {
Ok((name, fd))
} else {
syscall::open(&format!(":{}", fallback), syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK)
.map(|fd| (fallback, fd))
}
}
*/
mod scheme;
fn main() {
let mut args = env::args().skip(1);
@@ -45,48 +36,84 @@ fn main() {
//TODO: Figure out correct size of mapping, not just 256 * 1024
let address = unsafe { syscall::physmap(bar, 256 * 1024, PHYSMAP_WRITE | PHYSMAP_NO_CACHE).expect("nvmed: failed to map address") };
{
let mut nvme = Nvme::new(address).expect("nvmed: failed to allocate queues");
unsafe { nvme.init(); }
/*
let (_scheme_name, socket_fd) = create_scheme_fallback("disk", &name).expect("nvmed: failed to create disk scheme");
let mut socket = unsafe { File::from_raw_fd(socket_fd) };
syscall::fevent(socket_fd, EVENT_READ).expect("nvmed: failed to fevent disk scheme");
let event_fd = syscall::open("event:", syscall::O_RDWR | syscall::O_CLOEXEC)
.expect("nvmed: failed to open event queue");
let mut event_file = unsafe { File::from_raw_fd(event_fd as RawFd) };
let mut irq_file = File::open(&format!("irq:{}", irq)).expect("nvmed: failed to open irq file");
let irq_fd = irq_file.as_raw_fd();
syscall::fevent(irq_fd, EVENT_READ).expect("nvmed: failed to fevent irq file");
let irq_fd = syscall::open(
&format!("irq:{}", irq),
syscall::O_RDWR | syscall::O_NONBLOCK | syscall::O_CLOEXEC
).expect("nvmed: failed to open irq file");
syscall::write(event_fd, &syscall::Event {
id: irq_fd,
flags: syscall::EVENT_READ,
data: 0,
}).expect("nvmed: failed to watch irq file events");
let mut irq_file = unsafe { File::from_raw_fd(irq_fd as RawFd) };
let mut event_file = File::open("event:").expect("nvmed: failed to open event file");
let scheme = DiskScheme::new(nvme::disks(address, &name));
let scheme_name = format!("disk/{}", name);
let socket_fd = syscall::open(
&format!(":{}", scheme_name),
syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK | syscall::O_CLOEXEC
).expect("nvmed: failed to create disk scheme");
syscall::write(event_fd, &syscall::Event {
id: socket_fd,
flags: syscall::EVENT_READ,
data: 1,
}).expect("nvmed: failed to watch disk scheme events");
let mut socket_file = unsafe { File::from_raw_fd(socket_fd as RawFd) };
syscall::setrens(0, 0).expect("nvmed: failed to enter null namespace");
loop {
let mut nvme = Nvme::new(address).expect("nvmed: failed to allocate driver data");
let namespaces = unsafe { nvme.init() };
let mut scheme = DiskScheme::new(scheme_name, nvme, namespaces);
let mut todo = Vec::new();
'events: loop {
let mut event = Event::default();
if event_file.read(&mut event).expect("nvmed: failed to read event file") == 0 {
if event_file.read(&mut event).expect("nvmed: failed to read event queue") == 0 {
break;
}
if event.id == socket_fd {
loop {
let mut packet = Packet::default();
if socket.read(&mut packet).expect("nvmed: failed to read disk scheme") == 0 {
break;
match event.data {
0 => {
let mut irq = [0; 8];
if irq_file.read(&mut irq).expect("nvmed: failed to read irq file") >= irq.len() {
if scheme.irq() {
irq_file.write(&irq).expect("nvmed: failed to write irq file");
}
}
scheme.handle(&mut packet);
socket.write(&mut packet).expect("nvmed: failed to write disk scheme");
},
1 => loop {
let mut packet = Packet::default();
match socket_file.read(&mut packet) {
Ok(0) => break 'events,
Ok(_) => (),
Err(err) => match err.kind() {
ErrorKind::WouldBlock => break,
_ => Err(err).expect("nvmed: failed to read disk scheme"),
}
}
todo.push(packet);
},
unknown => {
panic!("nvmed: unknown event data {}", unknown);
},
}
let mut i = 0;
while i < todo.len() {
if let Some(a) = scheme.handle(&todo[i]) {
let mut packet = todo.remove(i);
packet.a = a;
socket_file.write(&packet).expect("nvmed: failed to write disk scheme");
} else {
i += 1;
}
} else if event.id == irq_fd {
let mut irq = [0; 8];
if irq_file.read(&mut irq).expect("nvmed: failed to read irq file") >= irq.len() {
//TODO : Test for IRQ
//irq_file.write(&irq).expect("nvmed: failed to write irq file");
}
} else {
println!("Unknown event {}", event.id);
}
}
*/
//TODO: destroy NVMe stuff
}
unsafe { let _ = syscall::physunmap(address); }
}
+145 -12
View File
@@ -1,6 +1,7 @@
use std::{mem, thread};
use std::thread;
use std::collections::BTreeMap;
use syscall::io::{Dma, Io, Mmio};
use syscall::error::Result;
use syscall::error::{Error, Result, EINVAL};
#[derive(Clone, Copy)]
#[repr(packed)]
@@ -124,7 +125,7 @@ impl NvmeCmd {
}
}
pub fn io_read(cid: u16, nsid: u32, lba: u64, count: u16, ptr: usize) -> Self {
pub fn io_read(cid: u16, nsid: u32, lba: u64, blocks_1: u16, ptr0: u64, ptr1: u64) -> Self {
Self {
opcode: 2,
flags: 1 << 6,
@@ -132,17 +133,17 @@ impl NvmeCmd {
nsid: nsid,
_rsvd: 0,
mptr: 0,
dptr: [ptr as u64, (count as u64) << 9],
dptr: [ptr0, ptr1],
cdw10: lba as u32,
cdw11: (lba >> 32) as u32,
cdw12: count.saturating_sub(1) as u32, //TODO: Prevent count of 0
cdw12: blocks_1 as u32,
cdw13: 0,
cdw14: 0,
cdw15: 0,
}
}
pub fn io_write(cid: u16, nsid: u32, lba: u64, count: u16, ptr: usize) -> Self {
pub fn io_write(cid: u16, nsid: u32, lba: u64, blocks_1: u16, ptr0: u64, ptr1: u64) -> Self {
Self {
opcode: 1,
flags: 1 << 6,
@@ -150,10 +151,10 @@ impl NvmeCmd {
nsid: nsid,
_rsvd: 0,
mptr: 0,
dptr: [ptr as u64, (count as u64) << 9],
dptr: [ptr0, ptr1],
cdw10: lba as u32,
cdw11: (lba >> 32) as u32,
cdw12: count.saturating_sub(1) as u32, //TODO: Prevent count of 0
cdw12: blocks_1 as u32,
cdw13: 0,
cdw14: 0,
cdw15: 0,
@@ -261,10 +262,18 @@ impl NvmeCompQueue {
}
}
pub struct NvmeNamespace {
pub id: u32,
pub blocks: u64,
pub block_size: u64,
}
pub struct Nvme {
regs: &'static mut NvmeRegs,
submission_queues: [NvmeCmdQueue; 2],
completion_queues: [NvmeCompQueue; 2],
buffer: Dma<[u8; 512 * 4096]>, // 2MB of buffer
buffer_prp: Dma<[u64; 512]>, // 4KB of PRP for the buffer
}
impl Nvme {
@@ -273,6 +282,8 @@ impl Nvme {
regs: unsafe { &mut *(address as *mut NvmeRegs) },
submission_queues: [NvmeCmdQueue::new()?, NvmeCmdQueue::new()?],
completion_queues: [NvmeCompQueue::new()?, NvmeCompQueue::new()?],
buffer: Dma::zeroed()?,
buffer_prp: Dma::zeroed()?,
})
}
@@ -292,7 +303,11 @@ impl Nvme {
self.doorbell(2 * (qid as usize) + 1).write(head as u32)
}
pub unsafe fn init(&mut self) {
pub unsafe fn init(&mut self) -> BTreeMap<u32, NvmeNamespace> {
for i in 0..self.buffer_prp.len() {
self.buffer_prp[i] = (self.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());
@@ -317,7 +332,7 @@ impl Nvme {
{
let asq = &self.submission_queues[0];
let acq = &self.completion_queues[0];
self.regs.aqa.write(((acq.data.len() as u32) << 16) | (asq.data.len() as u32));
self.regs.aqa.write(((acq.data.len() as u32 - 1) << 16) | (asq.data.len() as u32 - 1));
self.regs.asq.write(asq.data.physical() as u64);
self.regs.acq.write(acq.data.physical() as u64);
@@ -337,6 +352,7 @@ impl Nvme {
}
{
//TODO: Use buffer
let data: Dma<[u8; 4096]> = Dma::zeroed().unwrap();
println!(" - Attempting to identify controller");
@@ -391,6 +407,7 @@ impl Nvme {
let mut nsids = Vec::new();
{
//TODO: Use buffer
let data: Dma<[u32; 1024]> = Dma::zeroed().unwrap();
println!(" - Attempting to retrieve namespace ID list");
@@ -422,7 +439,9 @@ impl Nvme {
}
}
let mut namespaces = BTreeMap::new();
for &nsid in nsids.iter() {
//TODO: Use buffer
let data: Dma<[u8; 4096]> = Dma::zeroed().unwrap();
println!(" - Attempting to identify namespace {}", nsid);
@@ -454,6 +473,12 @@ impl Nvme {
println!(" - Capacity: {}", capacity);
//TODO: Read block size
namespaces.insert(nsid, NvmeNamespace {
id: nsid,
blocks: size,
block_size: 512, // TODO
});
}
for io_qid in 1..self.completion_queues.len() {
@@ -468,7 +493,7 @@ impl Nvme {
let queue = &mut self.submission_queues[qid];
let cid = queue.i as u16;
let entry = NvmeCmd::create_io_completion_queue(
cid, io_qid as u16, ptr, len as u16
cid, io_qid as u16, ptr, (len - 1) as u16
);
let tail = queue.submit(entry);
self.submission_queue_tail(qid as u16, tail as u16);
@@ -496,7 +521,7 @@ impl Nvme {
let cid = queue.i as u16;
//TODO: Get completion queue ID through smarter mechanism
let entry = NvmeCmd::create_io_submission_queue(
cid, io_qid as u16, ptr, len as u16, io_qid as u16
cid, io_qid as u16, ptr, (len - 1) as u16, io_qid as u16
);
let tail = queue.submit(entry);
self.submission_queue_tail(qid as u16, tail as u16);
@@ -510,5 +535,113 @@ impl Nvme {
self.completion_queue_head(qid as u16, head as u16);
}
}
namespaces
}
unsafe fn namespace_rw(&mut self, nsid: u32, lba: u64, blocks_1: u16, write: bool) -> Result<()> {
let (ptr0, ptr1) = if blocks_1 == 0 {
(self.buffer_prp[0], 0)
} else if blocks_1 == 1 {
(self.buffer_prp[0], self.buffer_prp[1])
} else {
(self.buffer_prp[0], (self.buffer_prp.physical() + 8) as u64)
};
{
let qid = 1;
let queue = &mut self.submission_queues[qid];
let cid = queue.i as u16;
//TODO: Get completion queue ID through smarter mechanism
let entry = if write {
NvmeCmd::io_write(
cid, nsid, lba, blocks_1, ptr0, ptr1
)
} else {
NvmeCmd::io_read(
cid, nsid, lba, blocks_1, ptr0, ptr1
)
};
let tail = queue.submit(entry);
self.submission_queue_tail(qid as u16, tail as u16);
}
{
let qid = 1;
let queue = &mut self.completion_queues[qid];
let (head, entry) = queue.complete_spin();
//TODO: Handle errors
self.completion_queue_head(qid as u16, head as u16);
}
Ok(())
}
pub unsafe fn namespace_read(&mut self, nsid: u32, lba: u64, buf: &mut [u8]) -> Result<Option<usize>> {
//TODO: Use interrupts
//TODO: Get real block size
let block_size = 512;
//TODO: Support this
if buf.len() % block_size != 0 {
return Err(Error::new(EINVAL));
}
//TODO: Support this
if buf.len() > self.buffer.len() {
return Err(Error::new(EINVAL));
}
let blocks = buf.len() / block_size;
if blocks == 0 {
return Ok(Some(0));
}
//TODO: Support this
if blocks > 0x1_0000 {
return Err(Error::new(EINVAL));
}
self.namespace_rw(nsid, lba, (blocks - 1) as u16, false)?;
buf.copy_from_slice(&self.buffer[..buf.len()]);
Ok(Some(buf.len()))
}
pub unsafe fn namespace_write(&mut self, nsid: u32, lba: u64, buf: &[u8]) -> Result<Option<usize>> {
//TODO: Use interrupts
//TODO: Get real block size
let block_size = 512;
//TODO: Support this
if buf.len() % block_size != 0 {
return Err(Error::new(EINVAL));
}
//TODO: Support this
if buf.len() > self.buffer.len() {
return Err(Error::new(EINVAL));
}
let blocks = buf.len() / block_size;
if blocks == 0 {
return Ok(Some(0));
}
//TODO: Support this
if blocks > 0x1_0000 {
return Err(Error::new(EINVAL));
}
self.buffer[..buf.len()].copy_from_slice(buf);
self.namespace_rw(nsid, lba, (blocks - 1) as u16, true)?;
Ok(Some(buf.len()))
}
}
+221
View File
@@ -0,0 +1,221 @@
use std::collections::BTreeMap;
use std::{cmp, str};
use std::fmt::Write;
use std::io::Read;
use syscall::{
Error, EACCES, EBADF, EINVAL, EISDIR, ENOENT, Result,
Io, SchemeBlockMut, Stat, MODE_DIR, MODE_FILE, O_DIRECTORY,
O_STAT, SEEK_CUR, SEEK_END, SEEK_SET};
use crate::nvme::{Nvme, NvmeNamespace};
#[derive(Clone)]
enum Handle {
List(Vec<u8>, usize),
Disk(u32, usize)
}
pub struct DiskScheme {
scheme_name: String,
nvme: Nvme,
disks: BTreeMap<u32, NvmeNamespace>,
handles: BTreeMap<usize, Handle>,
next_id: usize
}
impl DiskScheme {
pub fn new(scheme_name: String, nvme: Nvme, disks: BTreeMap<u32, NvmeNamespace>) -> DiskScheme {
DiskScheme {
scheme_name,
nvme,
disks,
handles: BTreeMap::new(),
next_id: 0
}
}
}
impl DiskScheme {
pub fn irq(&mut self) -> bool {
// TODO: implement
false
}
}
impl SchemeBlockMut for DiskScheme {
fn open(&mut self, path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result<Option<usize>> {
if uid == 0 {
let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?.trim_matches('/');
if path_str.is_empty() {
if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT {
let mut list = String::new();
for (nsid, _disk) in self.disks.iter() {
write!(list, "{}\n", nsid).unwrap();
}
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, Handle::List(list.into_bytes(), 0));
Ok(Some(id))
} else {
Err(Error::new(EISDIR))
}
} else {
let nsid = path_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
if self.disks.contains_key(&nsid) {
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, Handle::Disk(nsid, 0));
Ok(Some(id))
} else {
Err(Error::new(ENOENT))
}
}
} else {
Err(Error::new(EACCES))
}
}
fn dup(&mut self, id: usize, buf: &[u8]) -> Result<Option<usize>> {
if ! buf.is_empty() {
return Err(Error::new(EINVAL));
}
let new_handle = {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
handle.clone()
};
let new_id = self.next_id;
self.next_id += 1;
self.handles.insert(new_id, new_handle);
Ok(Some(new_id))
}
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
match *self.handles.get(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref data, _) => {
stat.st_mode = MODE_DIR;
stat.st_size = data.len() as u64;
Ok(Some(0))
},
Handle::Disk(number, _) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
stat.st_mode = MODE_FILE;
stat.st_size = disk.blocks * disk.block_size;
Ok(Some(0))
}
}
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
let mut i = 0;
let scheme_name = self.scheme_name.as_bytes();
let mut j = 0;
while i < buf.len() && j < scheme_name.len() {
buf[i] = scheme_name[j];
i += 1;
j += 1;
}
if i < buf.len() {
buf[i] = b':';
i += 1;
}
match *handle {
Handle::List(_, _) => (),
Handle::Disk(number, _) => {
let number_str = format!("{}", number);
let number_bytes = number_str.as_bytes();
j = 0;
while i < buf.len() && j < number_bytes.len() {
buf[i] = number_bytes[j];
i += 1;
j += 1;
}
}
}
Ok(Some(i))
}
fn read(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref mut handle, ref mut size) => {
let count = (&handle[*size..]).read(buf).unwrap();
*size += count;
Ok(Some(count))
},
Handle::Disk(number, ref mut size) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
let block_size = disk.block_size;
if let Some(count) = unsafe {
self.nvme.namespace_read(disk.id, (*size as u64)/block_size, buf)?
} {
*size += count;
Ok(Some(count))
} else {
Ok(None)
}
}
}
}
fn write(&mut self, id: usize, buf: &[u8]) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(_, _) => {
Err(Error::new(EBADF))
},
Handle::Disk(number, ref mut size) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
let block_size = disk.block_size;
if let Some(count) = unsafe {
self.nvme.namespace_write(disk.id, (*size as u64)/block_size, buf)?
} {
*size += count;
Ok(Some(count))
} else {
Ok(None)
}
}
}
}
fn seek(&mut self, id: usize, pos: usize, whence: usize) -> Result<Option<usize>> {
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
Handle::List(ref mut handle, ref mut size) => {
let len = handle.len() as usize;
*size = match whence {
SEEK_SET => cmp::min(len, pos),
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize,
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
_ => return Err(Error::new(EINVAL))
};
Ok(Some(*size))
},
Handle::Disk(number, ref mut size) => {
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
let len = (disk.blocks * disk.block_size) as usize;
*size = match whence {
SEEK_SET => cmp::min(len, pos),
SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize,
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
_ => return Err(Error::new(EINVAL))
};
Ok(Some(*size))
}
}
}
fn close(&mut self, id: usize) -> Result<Option<usize>> {
self.handles.remove(&id).ok_or(Error::new(EBADF)).and(Ok(Some(0)))
}
}