diff --git a/Cargo.toml b/Cargo.toml index 40c95020..c8447ba7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ device-api-macros = { path = "lib/device-api/macros" } # Drivers ygg_driver_pci = { path = "driver/bus/pci" } ygg_driver_nvme = { path = "driver/block/nvme" } +ygg_driver_block = { path = "driver/block/block" } kernel-fs = { path = "driver/fs/kernel-fs" } atomic_enum = "0.2.0" diff --git a/driver/block/block/Cargo.toml b/driver/block/block/Cargo.toml new file mode 100644 index 00000000..0513182a --- /dev/null +++ b/driver/block/block/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ygg_driver_block" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" } +kernel-util = { path = "../../../lib/kernel-util" } + +log = "0.4.20" diff --git a/driver/block/block/src/cache.rs b/driver/block/block/src/cache.rs new file mode 100644 index 00000000..cd724305 --- /dev/null +++ b/driver/block/block/src/cache.rs @@ -0,0 +1,57 @@ +use core::{fmt, mem::MaybeUninit}; + +use alloc::collections::BTreeMap; +use kernel_util::mem::PageBox; +use yggdrasil_abi::error::Error; + +// TODO LRU +pub struct BlockCache { + table: BTreeMap, + block_size: usize, +} + +pub type CachedBlock = PageBox<[u8]>; + +trait TryGetOrInsertWith { + fn try_get_or_insert_with Result>( + &mut self, + key: K, + f: F, + ) -> Result<&mut V, E>; +} + +impl BlockCache { + pub fn new(block_size: usize) -> Self { + log::debug!("Block cache created with bs={}", block_size); + Self { + table: BTreeMap::new(), + block_size, + } + } + + pub fn get_or_fetch_with< + E: From, + F: FnOnce(&mut PageBox<[MaybeUninit]>) -> Result<(), E>, + >( + &mut self, + index: K, + f: F, + ) -> Result<&mut CachedBlock, E> + where + K: Copy + fmt::Display, + { + if !self.table.contains_key(&index) { + let mut block = PageBox::new_uninit_slice(self.block_size)?; + log::debug!("Missed block with index {}, fetching", index); + f(&mut block)?; + self.table + .insert(index, unsafe { block.assume_init_slice() }); + } + + Ok(self.table.get_mut(&index).unwrap()) + } + + pub fn evict(&mut self, index: K) { + self.table.remove(&index); + } +} diff --git a/driver/block/block/src/lib.rs b/driver/block/block/src/lib.rs new file mode 100644 index 00000000..d89f6fb4 --- /dev/null +++ b/driver/block/block/src/lib.rs @@ -0,0 +1,39 @@ +#![no_std] + +extern crate alloc; + +use yggdrasil_abi::{error::Error, io::DeviceRequest}; + +pub mod cache; + +/// Block device interface +#[allow(unused)] +pub trait BlockDevice: Sync { + /// Reads data frmo the given offset of the device + fn read(&'static self, pos: u64, buf: &mut [u8]) -> Result { + Err(Error::NotImplemented) + } + /// Writes the data to the given offset of the device + fn write(&'static self, pos: u64, buf: &[u8]) -> Result { + Err(Error::NotImplemented) + } + + /// Returns the size of the block device in bytes + fn size(&self) -> Result { + Err(Error::NotImplemented) + } + + /// Returns `true` if the device can be read from + fn is_readable(&self) -> bool { + true + } + /// Returns `true` if the device can be written to + fn is_writable(&self) -> bool { + true + } + + /// Performs a device-specific function + fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> { + Err(Error::NotImplemented) + } +} diff --git a/driver/block/nvme/Cargo.toml b/driver/block/nvme/Cargo.toml index ca5721ce..725a668e 100644 --- a/driver/block/nvme/Cargo.toml +++ b/driver/block/nvme/Cargo.toml @@ -12,6 +12,7 @@ device-api = { path = "../../../lib/device-api", features = ["derive"] } vfs = { path = "../../../lib/vfs" } ygg_driver_pci = { path = "../../bus/pci" } +ygg_driver_block = { path = "../../block/block" } kernel-fs = { path = "../../fs/kernel-fs" } log = "0.4.20" diff --git a/driver/block/nvme/src/drive.rs b/driver/block/nvme/src/drive.rs index 559d61c0..cf788e62 100644 --- a/driver/block/nvme/src/drive.rs +++ b/driver/block/nvme/src/drive.rs @@ -1,15 +1,23 @@ +use core::mem::MaybeUninit; + use alloc::{boxed::Box, format}; use kernel_fs::devfs; -use vfs::BlockDevice; +use kernel_util::{ + block, + mem::{address::AsPhysicalAddress, PageBox}, + sync::IrqSafeSpinlock, +}; +use ygg_driver_block::{cache::BlockCache, BlockDevice}; use yggdrasil_abi::{error::Error, io::DeviceRequest}; -use crate::command::IdentifyNamespaceRequest; +use crate::{command::IdentifyNamespaceRequest, IoDirection}; use super::{error::NvmeError, NvmeController}; #[allow(unused)] pub struct NvmeDrive { controller: &'static NvmeController, + cache: IrqSafeSpinlock>, nsid: u32, total_lba_count: u64, lba_size: u64, @@ -37,6 +45,7 @@ impl NvmeDrive { let dev = Box::leak(Box::new(NvmeDrive { controller, + cache: IrqSafeSpinlock::new(BlockCache::new(lba_size as _)), nsid, total_lba_count, lba_size, @@ -49,11 +58,54 @@ impl NvmeDrive { Ok(dev) } + + async fn read_block( + &self, + lba: u64, + block: &mut PageBox<[MaybeUninit]>, + ) -> Result<(), NvmeError> { + self.controller + .perform_io( + self.nsid, + lba, + unsafe { block.as_physical_address() }, + IoDirection::Read, + ) + .await + } } impl BlockDevice for NvmeDrive { - fn read(&'static self, _pos: u64, _buf: &mut [u8]) -> Result { - todo!() + fn read(&'static self, mut pos: u64, buf: &mut [u8]) -> Result { + let mut cache = self.cache.lock(); + let mut rem = buf.len(); + let mut off = 0; + + while rem != 0 { + let lba = pos / self.lba_size; + + if lba == self.total_lba_count { + break; + } + + let block_offset = (pos % self.lba_size) as usize; + let count = core::cmp::min(self.lba_size as usize - block_offset, rem); + + let block = cache.get_or_fetch_with(lba, |block| { + block! { + self.read_block(lba, block).await + }? + .map_err(|_| Error::InvalidOperation) + })?; + + buf[off..off + count].copy_from_slice(&block[block_offset..block_offset + count]); + + rem -= count; + off += count; + pos += count as u64; + } + + Ok(off) } fn write(&'static self, _pos: u64, _buf: &[u8]) -> Result { diff --git a/driver/fs/kernel-fs/Cargo.toml b/driver/fs/kernel-fs/Cargo.toml index 59a737ee..d63a945f 100644 --- a/driver/fs/kernel-fs/Cargo.toml +++ b/driver/fs/kernel-fs/Cargo.toml @@ -10,4 +10,6 @@ yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" } vfs = { path = "../../../lib/vfs" } kernel-util = { path = "../../../lib/kernel-util" } +ygg_driver_block = { path = "../../block/block" } + log = "0.4.20" diff --git a/driver/fs/kernel-fs/src/devfs.rs b/driver/fs/kernel-fs/src/devfs.rs index 8cdd6955..5d9bb7f7 100644 --- a/driver/fs/kernel-fs/src/devfs.rs +++ b/driver/fs/kernel-fs/src/devfs.rs @@ -3,7 +3,8 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use alloc::{format, string::String}; use kernel_util::util::OneTimeInit; -use vfs::{impls::MemoryDirectory, BlockDevice, CharDevice, Node, NodeFlags, NodeRef}; +use vfs::{impls::MemoryDirectory, CharDevice, Node, NodeFlags, NodeRef}; +use ygg_driver_block::BlockDevice; use yggdrasil_abi::error::Error; /// Describes the kind of a character device diff --git a/lib/vfs/Cargo.toml b/lib/vfs/Cargo.toml index e4d187d8..22f37bae 100644 --- a/lib/vfs/Cargo.toml +++ b/lib/vfs/Cargo.toml @@ -9,6 +9,8 @@ edition = "2021" yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git", features = ["alloc"] } kernel-util = { path = "../kernel-util" } +ygg_driver_block = { path = "../../driver/block/block" } + log = "0.4.20" [dev-dependencies] diff --git a/lib/vfs/src/device.rs b/lib/vfs/src/device.rs index b76c6d6c..1015ed99 100644 --- a/lib/vfs/src/device.rs +++ b/lib/vfs/src/device.rs @@ -1,39 +1,8 @@ +use ygg_driver_block::BlockDevice; use yggdrasil_abi::{error::Error, io::DeviceRequest}; use crate::node::{CommonImpl, NodeRef}; -/// Block device interface -#[allow(unused)] -pub trait BlockDevice: Sync { - /// Reads data frmo the given offset of the device - fn read(&'static self, pos: u64, buf: &mut [u8]) -> Result { - Err(Error::NotImplemented) - } - /// Writes the data to the given offset of the device - fn write(&'static self, pos: u64, buf: &[u8]) -> Result { - Err(Error::NotImplemented) - } - - /// Returns the size of the block device in bytes - fn size(&self) -> Result { - Err(Error::NotImplemented) - } - - /// Returns `true` if the device can be read from - fn is_readable(&self) -> bool { - true - } - /// Returns `true` if the device can be written to - fn is_writable(&self) -> bool { - true - } - - /// Performs a device-specific function - fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> { - Err(Error::NotImplemented) - } -} - /// Character device interface #[allow(unused)] pub trait CharDevice: Sync { diff --git a/lib/vfs/src/lib.rs b/lib/vfs/src/lib.rs index 7b265746..8b836512 100644 --- a/lib/vfs/src/lib.rs +++ b/lib/vfs/src/lib.rs @@ -17,7 +17,7 @@ pub(crate) mod node; pub(crate) mod path; pub(crate) mod traits; -pub use device::{BlockDevice, CharDevice}; +pub use device::CharDevice; pub use file::{DirectoryOpenPosition, File, FileRef, InstanceData}; pub use ioctx::{Action, IoContext}; pub use node::{ diff --git a/lib/vfs/src/node/mod.rs b/lib/vfs/src/node/mod.rs index 9e5a76e4..2030bd52 100644 --- a/lib/vfs/src/node/mod.rs +++ b/lib/vfs/src/node/mod.rs @@ -2,6 +2,7 @@ use core::{any::Any, fmt}; use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec}; use kernel_util::sync::IrqSafeSpinlock; +use ygg_driver_block::BlockDevice; use yggdrasil_abi::{ bitflags, error::Error, @@ -19,7 +20,7 @@ mod tree; pub use access::AccessToken; pub use traits::{CommonImpl, DirectoryImpl, RegularImpl, SymlinkImpl}; -use crate::device::{BlockDevice, BlockDeviceWrapper, CharDevice, CharDeviceWrapper}; +use crate::device::{BlockDeviceWrapper, CharDevice, CharDeviceWrapper}; /// Wrapper type for a [Node] shared reference pub type NodeRef = Arc; @@ -295,7 +296,7 @@ mod tests { use core::any::Any; use std::sync::Arc; - use crate::{node::NodeFlags, NodeRef}; + use crate::node::NodeFlags; use super::{CommonImpl, DirectoryImpl, Node, RegularImpl};