dev/block: add a simple block subsystem

This commit is contained in:
Mark Poliakov 2023-12-11 00:58:11 +02:00
parent 8b7a7b9295
commit 5ffd4ca4e2
12 changed files with 177 additions and 40 deletions

View File

@ -22,6 +22,7 @@ device-api-macros = { path = "lib/device-api/macros" }
# Drivers # Drivers
ygg_driver_pci = { path = "driver/bus/pci" } ygg_driver_pci = { path = "driver/bus/pci" }
ygg_driver_nvme = { path = "driver/block/nvme" } ygg_driver_nvme = { path = "driver/block/nvme" }
ygg_driver_block = { path = "driver/block/block" }
kernel-fs = { path = "driver/fs/kernel-fs" } kernel-fs = { path = "driver/fs/kernel-fs" }
atomic_enum = "0.2.0" atomic_enum = "0.2.0"

View File

@ -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"

View File

@ -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<K: Ord + Eq> {
table: BTreeMap<K, CachedBlock>,
block_size: usize,
}
pub type CachedBlock = PageBox<[u8]>;
trait TryGetOrInsertWith<K, V> {
fn try_get_or_insert_with<E, F: FnOnce() -> Result<V, E>>(
&mut self,
key: K,
f: F,
) -> Result<&mut V, E>;
}
impl<K: Ord + Eq> BlockCache<K> {
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<Error>,
F: FnOnce(&mut PageBox<[MaybeUninit<u8>]>) -> 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);
}
}

View File

@ -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<usize, Error> {
Err(Error::NotImplemented)
}
/// Writes the data to the given offset of the device
fn write(&'static self, pos: u64, buf: &[u8]) -> Result<usize, Error> {
Err(Error::NotImplemented)
}
/// Returns the size of the block device in bytes
fn size(&self) -> Result<u64, Error> {
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)
}
}

View File

@ -12,6 +12,7 @@ device-api = { path = "../../../lib/device-api", features = ["derive"] }
vfs = { path = "../../../lib/vfs" } vfs = { path = "../../../lib/vfs" }
ygg_driver_pci = { path = "../../bus/pci" } ygg_driver_pci = { path = "../../bus/pci" }
ygg_driver_block = { path = "../../block/block" }
kernel-fs = { path = "../../fs/kernel-fs" } kernel-fs = { path = "../../fs/kernel-fs" }
log = "0.4.20" log = "0.4.20"

View File

@ -1,15 +1,23 @@
use core::mem::MaybeUninit;
use alloc::{boxed::Box, format}; use alloc::{boxed::Box, format};
use kernel_fs::devfs; 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 yggdrasil_abi::{error::Error, io::DeviceRequest};
use crate::command::IdentifyNamespaceRequest; use crate::{command::IdentifyNamespaceRequest, IoDirection};
use super::{error::NvmeError, NvmeController}; use super::{error::NvmeError, NvmeController};
#[allow(unused)] #[allow(unused)]
pub struct NvmeDrive { pub struct NvmeDrive {
controller: &'static NvmeController, controller: &'static NvmeController,
cache: IrqSafeSpinlock<BlockCache<u64>>,
nsid: u32, nsid: u32,
total_lba_count: u64, total_lba_count: u64,
lba_size: u64, lba_size: u64,
@ -37,6 +45,7 @@ impl NvmeDrive {
let dev = Box::leak(Box::new(NvmeDrive { let dev = Box::leak(Box::new(NvmeDrive {
controller, controller,
cache: IrqSafeSpinlock::new(BlockCache::new(lba_size as _)),
nsid, nsid,
total_lba_count, total_lba_count,
lba_size, lba_size,
@ -49,11 +58,54 @@ impl NvmeDrive {
Ok(dev) Ok(dev)
} }
async fn read_block(
&self,
lba: u64,
block: &mut PageBox<[MaybeUninit<u8>]>,
) -> Result<(), NvmeError> {
self.controller
.perform_io(
self.nsid,
lba,
unsafe { block.as_physical_address() },
IoDirection::Read,
)
.await
}
} }
impl BlockDevice for NvmeDrive { impl BlockDevice for NvmeDrive {
fn read(&'static self, _pos: u64, _buf: &mut [u8]) -> Result<usize, Error> { fn read(&'static self, mut pos: u64, buf: &mut [u8]) -> Result<usize, Error> {
todo!() 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<usize, Error> { fn write(&'static self, _pos: u64, _buf: &[u8]) -> Result<usize, Error> {

View File

@ -10,4 +10,6 @@ yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
vfs = { path = "../../../lib/vfs" } vfs = { path = "../../../lib/vfs" }
kernel-util = { path = "../../../lib/kernel-util" } kernel-util = { path = "../../../lib/kernel-util" }
ygg_driver_block = { path = "../../block/block" }
log = "0.4.20" log = "0.4.20"

View File

@ -3,7 +3,8 @@ use core::sync::atomic::{AtomicUsize, Ordering};
use alloc::{format, string::String}; use alloc::{format, string::String};
use kernel_util::util::OneTimeInit; 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; use yggdrasil_abi::error::Error;
/// Describes the kind of a character device /// Describes the kind of a character device

View File

@ -9,6 +9,8 @@ edition = "2021"
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git", features = ["alloc"] } yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git", features = ["alloc"] }
kernel-util = { path = "../kernel-util" } kernel-util = { path = "../kernel-util" }
ygg_driver_block = { path = "../../driver/block/block" }
log = "0.4.20" log = "0.4.20"
[dev-dependencies] [dev-dependencies]

View File

@ -1,39 +1,8 @@
use ygg_driver_block::BlockDevice;
use yggdrasil_abi::{error::Error, io::DeviceRequest}; use yggdrasil_abi::{error::Error, io::DeviceRequest};
use crate::node::{CommonImpl, NodeRef}; 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<usize, Error> {
Err(Error::NotImplemented)
}
/// Writes the data to the given offset of the device
fn write(&'static self, pos: u64, buf: &[u8]) -> Result<usize, Error> {
Err(Error::NotImplemented)
}
/// Returns the size of the block device in bytes
fn size(&self) -> Result<u64, Error> {
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 /// Character device interface
#[allow(unused)] #[allow(unused)]
pub trait CharDevice: Sync { pub trait CharDevice: Sync {

View File

@ -17,7 +17,7 @@ pub(crate) mod node;
pub(crate) mod path; pub(crate) mod path;
pub(crate) mod traits; pub(crate) mod traits;
pub use device::{BlockDevice, CharDevice}; pub use device::CharDevice;
pub use file::{DirectoryOpenPosition, File, FileRef, InstanceData}; pub use file::{DirectoryOpenPosition, File, FileRef, InstanceData};
pub use ioctx::{Action, IoContext}; pub use ioctx::{Action, IoContext};
pub use node::{ pub use node::{

View File

@ -2,6 +2,7 @@ use core::{any::Any, fmt};
use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec}; use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
use kernel_util::sync::IrqSafeSpinlock; use kernel_util::sync::IrqSafeSpinlock;
use ygg_driver_block::BlockDevice;
use yggdrasil_abi::{ use yggdrasil_abi::{
bitflags, bitflags,
error::Error, error::Error,
@ -19,7 +20,7 @@ mod tree;
pub use access::AccessToken; pub use access::AccessToken;
pub use traits::{CommonImpl, DirectoryImpl, RegularImpl, SymlinkImpl}; 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 /// Wrapper type for a [Node] shared reference
pub type NodeRef = Arc<Node>; pub type NodeRef = Arc<Node>;
@ -295,7 +296,7 @@ mod tests {
use core::any::Any; use core::any::Any;
use std::sync::Arc; use std::sync::Arc;
use crate::{node::NodeFlags, NodeRef}; use crate::node::NodeFlags;
use super::{CommonImpl, DirectoryImpl, Node, RegularImpl}; use super::{CommonImpl, DirectoryImpl, Node, RegularImpl};