nvme: proper handling for PRPs

This commit is contained in:
Mark Poliakov 2024-12-05 11:47:38 +02:00
parent f9ab1bece3
commit 93c143fa58
14 changed files with 324 additions and 189 deletions

View File

@ -247,11 +247,12 @@ impl<const N: usize> fmt::Debug for String<N> {
impl Command for IoRead {
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
assert!(self.count < 65536);
assert_ne!(self.count, 0);
sqe.command.set_opcode(0x02);
sqe.command_specific[0] = self.lba as u32;
sqe.command_specific[1] = (self.lba >> 32) as u32;
sqe.command_specific[2] = self.count;
sqe.command_specific[2] = self.count - 1;
sqe.nsid = self.nsid;
}
}
@ -259,11 +260,12 @@ impl Command for IoRead {
impl Command for IoWrite {
fn fill_sqe(&self, sqe: &mut SubmissionQueueEntry) {
assert!(self.count < 65536);
assert_ne!(self.count, 0);
sqe.command.set_opcode(0x01);
sqe.command_specific[0] = self.lba as u32;
sqe.command_specific[1] = (self.lba >> 32) as u32;
sqe.command_specific[2] = self.count;
sqe.command_specific[2] = self.count - 1;
sqe.nsid = self.nsid;
}
}

View File

@ -16,12 +16,14 @@ pub struct NvmeDrive {
nsid: u32,
total_lba_count: u64,
lba_size: u64,
max_lba_per_request: usize,
}
impl NvmeDrive {
pub async fn create(
controller: &'static NvmeController,
nsid: u32,
max_transfer_size: usize,
) -> Result<&'static NvmeDrive, NvmeError> {
let admin_q = controller.admin_q.get();
let identify = admin_q.request(IdentifyNamespaceRequest { nsid }).await?;
@ -30,12 +32,14 @@ impl NvmeDrive {
let current_lba_format = identify.lba_fmt(current_lba_format_idx).unwrap();
let lba_size = current_lba_format.lba_data_size().unwrap();
let total_lba_count = identify.total_lba_count();
let max_lba_per_request = (max_transfer_size / lba_size as usize).min(512);
log::debug!(
"ns = {}, lba = {}B, size = {}M",
"ns = {}, lba = {}B, size = {}M, max lba/req = {}",
nsid,
lba_size,
(total_lba_count * lba_size) / (1024 * 1024)
(total_lba_count * lba_size) / (1024 * 1024),
max_lba_per_request,
);
let dev = Box::leak(Box::new(NvmeDrive {
@ -43,6 +47,7 @@ impl NvmeDrive {
nsid,
total_lba_count,
lba_size,
max_lba_per_request,
}));
let node_name = format!("nvme{}n{}", controller.controller_id.get(), nsid);
@ -73,10 +78,17 @@ impl NgBlockDevice for NvmeDrive {
let result = self
.controller
.perform_io(self.nsid, lba, lba_count, buffer_address, IoDirection::Read)
.perform_io(
self.nsid,
lba,
lba_count,
buffer_address,
buffer.len(),
IoDirection::Read,
)
.await;
//log::info!(target: "io", "read #{lba}, {lba_count} blocks -> {result:?} @ {buffer_address:#x}");
log::info!(target: "io", "read #{lba}, {lba_count} blocks -> {result:?} @ {buffer_address:#x}");
result
}
@ -93,6 +105,7 @@ impl NgBlockDevice for NvmeDrive {
lba,
lba_count,
buffer_address,
buffer.len(),
IoDirection::Write,
)
.await;
@ -110,7 +123,6 @@ impl NgBlockDevice for NvmeDrive {
}
fn max_blocks_per_request(&self) -> usize {
// TODO get from device
8
self.max_lba_per_request
}
}

View File

@ -1,9 +1,12 @@
use libk_mm::address::PhysicalAddress;
use yggdrasil_abi::error::Error;
use super::queue::CommandError;
#[derive(Debug)]
pub enum NvmeError {
InvalidBuffer(PhysicalAddress, usize),
RequestTooLarge(usize),
MemoryError(Error),
CommandError(CommandError),
}
@ -17,6 +20,8 @@ impl From<CommandError> for NvmeError {
impl From<NvmeError> for Error {
fn from(value: NvmeError) -> Self {
match value {
NvmeError::RequestTooLarge(_) => Error::InvalidArgument,
NvmeError::InvalidBuffer(_, _) => Error::InvalidArgument,
NvmeError::MemoryError(err) => err,
// TODO Error::DeviceError
NvmeError::CommandError(_err) => Error::InvalidArgument,

View File

@ -18,11 +18,12 @@ use device_api::{
};
use drive::NvmeDrive;
use libk::task::{cpu_count, cpu_index, runtime};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo, L3_PAGE_SIZE};
use libk_util::{
sync::{IrqGuard, IrqSafeSpinlock},
OneTimeInit,
};
use queue::PrpList;
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
@ -50,6 +51,10 @@ mod drive;
mod error;
mod queue;
pub const MAX_PAGES_PER_REQUEST: usize = 256;
// Use host page
pub const PAGE_SIZE: usize = L3_PAGE_SIZE;
register_bitfields! {
u32,
CC [
@ -127,6 +132,7 @@ pub struct NvmeController {
pci: PciDeviceInfo,
doorbell_shift: usize,
min_page_size: usize,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
@ -220,19 +226,27 @@ impl NvmeController {
let admin_q = self.admin_q.get();
// Identify the controller
let _identify = admin_q.request(IdentifyControllerRequest).await?;
let identify = admin_q.request(IdentifyControllerRequest).await?;
// TODO do something with identify_controller
let max_transfer_size = if identify.mdts == 0 {
// Pick some sane default value
256 * PAGE_SIZE
} else {
(1 << identify.mdts) * PAGE_SIZE
};
self.create_queues().await?;
// Identify namespaces
self.enumerate_namespaces().await?;
self.enumerate_namespaces(max_transfer_size).await?;
Ok(())
}
async fn enumerate_namespaces(&'static self) -> Result<(), NvmeError> {
async fn enumerate_namespaces(
&'static self,
max_transfer_size: usize,
) -> Result<(), NvmeError> {
let admin_q = self.admin_q.get();
let namespaces = admin_q
@ -243,7 +257,7 @@ impl NvmeController {
let list = &namespaces.entries[..count];
for &nsid in list {
match NvmeDrive::create(self, nsid).await {
match NvmeDrive::create(self, nsid, max_transfer_size).await {
Ok(drive) => {
self.drive_table.lock().insert(nsid, drive);
}
@ -262,20 +276,15 @@ impl NvmeController {
lba: u64,
lba_count: usize,
buffer_address: PhysicalAddress,
transfer_size: usize,
direction: IoDirection,
) -> Result<(), NvmeError> {
let prp_list = PrpList::from_buffer(buffer_address, transfer_size)?;
let _guard = IrqGuard::acquire();
let cpu_index = cpu_index();
let ioq = &self.ioqs.get()[cpu_index as usize];
// log::debug!(
// "{:?} ioq #{}, nsid={}, lba={:#x}",
// direction,
// cpu_index,
// nsid,
// lba
// );
let cmd_id = match direction {
IoDirection::Read => ioq.submit(
IoRead {
@ -283,21 +292,21 @@ impl NvmeController {
lba,
count: lba_count as _,
},
&[buffer_address],
&prp_list,
true,
),
)?,
IoDirection::Write => ioq.submit(
IoWrite {
nsid,
lba,
count: lba_count as _,
},
&[buffer_address],
&prp_list,
true,
),
)?,
};
ioq.wait_for_completion(cmd_id, ()).await?;
ioq.wait_for_completion(cmd_id, &prp_list, ()).await?;
Ok(())
}
@ -330,12 +339,6 @@ impl Device for NvmeController {
unsafe fn init(&'static self) -> Result<(), Error> {
let regs = self.regs.lock();
let min_page_size = 1usize << (12 + regs.CAP.read(CAP::MPSMIN));
if min_page_size > 4096 {
panic!();
}
let timeout = Duration::from_millis(regs.CAP.read(CAP::TO) * 500);
log::debug!("Worst-case timeout: {:?}", timeout);
@ -434,6 +437,12 @@ pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
regs.CC.modify(CC::ENABLE::CLEAR);
let doorbell_shift = regs.CAP.read(CAP::DSTRD) as usize + 1;
let min_page_size = 1 << (regs.CAP.read(CAP::MPSMIN) + 12);
if min_page_size > PAGE_SIZE {
log::error!("Cannot support NVMe HC: min page size ({min_page_size}) > host page size ({PAGE_SIZE})");
return Err(Error::InvalidArgument);
}
Ok(Box::leak(Box::new(NvmeController {
regs: IrqSafeSpinlock::new(regs),
@ -446,6 +455,7 @@ pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
io_queue_count: AtomicUsize::new(1),
doorbell_shift,
min_page_size,
})))
}

View File

@ -94,6 +94,37 @@ pub struct QueuePair {
inner: IrqSafeSpinlock<Inner>,
}
pub enum PrpList {
None,
One(PhysicalAddress),
Two(PhysicalAddress, PhysicalAddress),
List(PhysicalAddress, PageBox<[PhysicalAddress]>),
}
impl PrpList {
pub fn from_buffer(base: PhysicalAddress, size: usize) -> Result<Self, NvmeError> {
// TODO hardcoded page size
if base.into_u64() % 0x1000 != 0 {
todo!();
}
match size {
0 => Ok(Self::None),
_ if size <= 0x1000 => Ok(Self::One(base)),
_ if size <= 0x2000 => Ok(Self::Two(base, base.add(0x1000))),
_ => {
let count = (size + 0xFFF) / 0x1000;
let list = PageBox::new_slice_with(|i| base.add((i + 1) * 0x1000), count - 1)
.map_err(NvmeError::MemoryError)?;
Ok(Self::List(base, list))
}
}
}
pub fn none() -> Self {
Self::None
}
}
const_assert!(size_of::<CompletionQueueEntry>().is_power_of_two());
impl PhysicalRegionPage {
@ -280,9 +311,12 @@ impl QueuePair {
pub async fn wait_for_completion<T: Unpin>(
&self,
command_id: u32,
list: &PrpList,
result: T,
) -> Result<T, CommandError> {
let mut response = Some(result);
// NOTE: for multiple blocks supplied via the PRP list, the NVMe controller will signal
// a completion per each block
poll_fn(|cx| {
let mut inner = self.inner.lock();
@ -304,20 +338,33 @@ impl QueuePair {
.await
}
pub fn submit<C: Command>(&self, cmd: C, ranges: &[PhysicalAddress], set_pending: bool) -> u32 {
pub fn submit<C: Command>(
&self,
cmd: C,
ranges: &PrpList,
set_pending: bool,
) -> Result<u32, NvmeError> {
let mut inner = self.inner.lock();
let mut sqe = SubmissionQueueEntry::zeroed();
match ranges.len() {
1 => {
sqe.data_pointer[0] = PhysicalRegionPage::with_addr(ranges[0]);
sqe.data_pointer[1] = PhysicalRegionPage::null();
}
0 => {
match ranges {
PrpList::None => {
sqe.data_pointer[0] = PhysicalRegionPage::null();
sqe.data_pointer[1] = PhysicalRegionPage::null();
}
_ => todo!(),
&PrpList::One(buf0) => {
sqe.data_pointer[0] = PhysicalRegionPage::with_addr(buf0);
sqe.data_pointer[1] = PhysicalRegionPage::null();
}
&PrpList::Two(buf0, buf1) => {
sqe.data_pointer[0] = PhysicalRegionPage::with_addr(buf0);
sqe.data_pointer[1] = PhysicalRegionPage::with_addr(buf1);
}
PrpList::List(buf0, list) => {
sqe.data_pointer[0] = PhysicalRegionPage::with_addr(*buf0);
sqe.data_pointer[1] =
PhysicalRegionPage::with_addr(unsafe { list.as_physical_address() });
}
}
cmd.fill_sqe(&mut sqe);
@ -331,15 +378,15 @@ impl QueuePair {
inner.sq.enqueue(sqe);
command_id
Ok(command_id)
}
pub fn request_no_data<C: Command>(
&self,
req: C,
) -> impl Future<Output = Result<(), CommandError>> + '_ {
let command_id = self.submit(req, &[], true);
self.wait_for_completion(command_id, ())
pub async fn request_no_data<C: Command>(&self, req: C) -> Result<(), NvmeError> {
let list = PrpList::None;
let command_id = self.submit(req, &list, true)?;
self.wait_for_completion(command_id, &list, ())
.await
.map_err(NvmeError::CommandError)
}
pub async fn request<'r, R: Request>(
@ -350,8 +397,11 @@ impl QueuePair {
R::Response: 'r,
{
let response = PageBox::new_uninit().map_err(NvmeError::MemoryError)?;
let command_id = self.submit(req, &[unsafe { response.as_physical_address() }], true);
let result = self.wait_for_completion(command_id, response).await?;
let list = PrpList::from_buffer(unsafe { response.as_physical_address() }, size_of::<R>())?;
let command_id = self.submit(req, &list, true)?;
let result = self
.wait_for_completion(command_id, &list, response)
.await?;
Ok(unsafe { result.assume_init() })
}

View File

@ -41,7 +41,7 @@ pub struct BlockCache<
> {
device: &'static dyn BlockDevice,
block_size: usize,
cache: AsyncMutex<LruCache<u64, Arc<IrqSafeRwLock<CachedBlock<A>>>>>,
_pd: PhantomData<A>,
}
impl DeviceMapper {
@ -64,8 +64,9 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DeviceMapper<A> {
block_size: usize,
bucket_capacity: usize,
) -> DeviceMapper<A> {
let cache = BlockCache::<A>::with_capacity_in(device, block_size, bucket_capacity);
DeviceMapper::<A>::Cached(cache)
todo!()
// let cache = BlockCache::<A>::with_capacity_in(device, block_size, bucket_capacity);
// DeviceMapper::<A>::Cached(cache)
}
pub fn uncached_in(device: &'static dyn BlockDevice, block_size: usize) -> DeviceMapper<A> {
@ -83,7 +84,7 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DeviceMapper<A> {
pub fn device(&self) -> &'static dyn BlockDevice {
match self {
Self::Uncached(uncache) => uncache.device(),
Self::Cached(cache) => cache.device(),
Self::Cached(_cache) => todo!(),
}
}
@ -94,7 +95,7 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DeviceMapper<A> {
) -> Result<T, Error> {
match self {
Self::Uncached(uncache) => uncache.try_with(pos, mapper).await,
Self::Cached(cache) => cache.try_with(pos, mapper).await,
Self::Cached(_cache) => todo!(),
}
}
@ -105,17 +106,14 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DeviceMapper<A> {
) -> Result<T, Error> {
match self {
Self::Uncached(uncache) => uncache.try_with_mut(pos, mapper).await,
Self::Cached(cache) => cache.try_with_mut(pos, mapper).await,
Self::Cached(_cache) => todo!(),
}
}
pub async fn flush(&self) -> Result<(), Error> {
match self {
Self::Uncached(_) => Ok(()),
Self::Cached(cache) => {
cache.flush().await;
Ok(())
}
Self::Cached(_cache) => todo!(),
}
}
}
@ -150,97 +148,6 @@ impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> UncachedCache<A> {
}
}
impl BlockCache {
pub fn with_capacity(
device: &'static dyn BlockDevice,
block_size: usize,
bucket_capacity: usize,
) -> BlockCache {
BlockCache::with_capacity_in(device, block_size, bucket_capacity)
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> BlockCache<A> {
pub fn with_capacity_in(
device: &'static dyn BlockDevice,
block_size: usize,
bucket_capacity: usize,
) -> BlockCache<A> {
if block_size % device.block_size() != 0 {
panic!("Cache block size is not multiple of device block size");
}
BlockCache {
device,
block_size,
cache: AsyncMutex::new(LruCache::with_capacity(bucket_capacity, 8)),
}
}
pub fn device(&self) -> &'static dyn BlockDevice {
self.device
}
async fn evict_block(&self, pos: u64, block: Arc<IrqSafeRwLock<CachedBlock<A>>>) {
let read = block.read();
if read.dirty {
if let Err(err) = self.device.write_aligned(pos, read.data.as_slice()).await {
log::error!("Disk error: flushing block {}: {:?}", pos, err);
}
}
}
async fn fetch_block(&self, pos: u64) -> Result<Arc<IrqSafeRwLock<CachedBlock<A>>>, Error> {
let mut data = PageBox::new_uninit_slice_in(self.block_size)?;
self.device.read_aligned(pos, data.as_slice_mut()).await?;
let data = unsafe { data.assume_init_slice() };
Ok(Arc::new(IrqSafeRwLock::new(CachedBlock {
data,
dirty: false,
})))
}
async fn entry(&self, pos: u64) -> Result<Arc<IrqSafeRwLock<CachedBlock<A>>>, Error> {
let mut lock = self.cache.lock().await;
let (value, evicted) = lock
.try_get_or_insert_with_async(pos, || self.fetch_block(pos))
.await?;
if let Some((pos, block)) = evicted {
self.evict_block(pos, block).await;
}
Ok(value.clone())
}
pub async fn try_with<T, F: FnOnce(&[u8]) -> Result<T, Error>>(
&self,
pos: u64,
mapper: F,
) -> Result<T, Error> {
let block = self.entry(pos).await?;
let result = mapper(&block.read()[..])?;
Ok(result)
}
pub async fn try_with_mut<T, F: FnOnce(&mut [u8]) -> Result<T, Error>>(
&self,
pos: u64,
mapper: F,
) -> Result<T, Error> {
let block = self.entry(pos).await?;
let mut block = block.write();
let result = mapper(&mut block[..])?;
block.dirty = true;
Ok(result)
}
pub async fn flush(&self) {
for (pos, block) in self.cache.lock().await.flush() {
self.evict_block(pos, block).await;
}
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> CachedBlock<A> {
pub fn set_dirty(&mut self) {
self.dirty = true;

View File

@ -125,31 +125,48 @@ impl<'a, D: NgBlockDevice + 'a> BlockDevice for NgBlockDeviceWrapper<'a, D> {
}
async fn read(&self, mut pos: u64, mut buf: &mut [u8]) -> Result<usize, Error> {
let len = buf.len();
let start_lba = pos / self.block_size as u64;
let end_lba = (pos + buf.len() as u64)
.div_ceil(self.block_size as u64)
.min(self.block_count as u64);
if start_lba >= end_lba {
return Ok(0);
}
let mut remaining = buf.len();
let mut remaining_lba = (end_lba - start_lba) as usize;
let mut lba = start_lba;
let mut offset = 0;
let max_lba_count = remaining_lba.min(self.max_blocks_per_request);
let mut buffer = PageBox::new_uninit_slice(max_lba_count * self.block_size)?;
while remaining != 0 {
let mut block = PageBox::new_uninit_slice(self.block_size)?;
let block_offset = pos as usize % self.block_size;
let lba = pos / self.block_size as u64;
let amount = core::cmp::min(self.block_size - block_offset, buf.len());
let block_count = remaining_lba.min(self.max_blocks_per_request);
let block_offset = (pos % self.block_size as u64) as usize;
let amount = (block_count * self.block_size - block_offset).min(remaining);
self.device
.read(lba, block.as_slice_mut())
.read(lba, buffer.as_slice_mut())
.await
.map_err(Self::handle_drive_error)?;
let block = unsafe { block.assume_init_slice() };
let data = unsafe {
MaybeUninit::slice_assume_init_ref(&buffer[block_offset..block_offset + amount])
};
buf[..amount].copy_from_slice(&block[block_offset..block_offset + amount]);
buf = &mut buf[amount..];
buf[offset..offset + amount].copy_from_slice(data);
lba += block_count as u64;
remaining_lba -= block_count;
remaining -= amount;
pos += amount as u64;
offset += amount;
}
Ok(len)
assert_eq!(remaining_lba, 0);
Ok(offset)
}
async fn write(&self, mut pos: u64, mut buf: &[u8]) -> Result<usize, Error> {

View File

@ -185,6 +185,12 @@ pub(crate) async fn probe_gpt<D: NgBlockDevice + 'static>(
break;
}
log::debug!(
"Found partition: guid={}, start={}, end={}",
pt_entry.type_guid,
pt_entry.lba_start,
pt_entry.lba_end
);
partitions.push(Partition {
device: dev,
lba_start: pt_entry.lba_start,

View File

@ -6,8 +6,8 @@ use abi::path::Path;
use ext2::Ext2Fs;
use kernel_fs::devfs;
use libk::{
block, random,
vfs::{self, impls::read_fn_node, register_root, IoContext, NodeRef},
block,
vfs::{self, register_root, IoContext, NodeRef},
};
use libk_mm::{
address::{PhysicalAddress, Virtualize},
@ -18,7 +18,10 @@ use memfs::block::{self, BlockAllocator};
use static_assertions::const_assert_eq;
use yggdrasil_abi::{error::Error, io::MountOptions};
pub use pseudo::add_pseudo_devices;
// pub mod devfs;
pub mod pseudo;
pub mod sysfs;
/// Describes in-memory filesystem image used as initial root
@ -83,16 +86,3 @@ pub fn create_filesystem(ioctx: &mut IoContext, options: &MountOptions) -> Resul
Ok(root)
}
/// Adds "pseudo"-devices to the filesystem (i.e. /dev/random)
pub fn add_pseudo_devices() -> Result<(), Error> {
let random = read_fn_node(move |_, buf| {
random::read(buf);
Ok(buf.len())
});
let root = devfs::root();
root.add_child("random", random)?;
Ok(())
}

85
kernel/src/fs/pseudo.rs Normal file
View File

@ -0,0 +1,85 @@
//! Pseudo-devices
use core::task::{Context, Poll};
use abi::error::Error;
use alloc::boxed::Box;
use async_trait::async_trait;
use kernel_fs::devfs;
use libk::{
random,
vfs::{impls::read_fn_node, CharDevice, FileReadiness},
};
struct Null;
struct Zero;
impl FileReadiness for Null {
fn poll_read(&self, _cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
Poll::Ready(Ok(()))
}
}
#[async_trait]
impl CharDevice for Null {
async fn read(&'static self, _data: &mut [u8]) -> Result<usize, Error> {
Ok(0)
}
async fn write(&'static self, data: &[u8]) -> Result<usize, Error> {
Ok(data.len())
}
fn read_nonblocking(&self, _data: &mut [u8]) -> Result<usize, Error> {
Ok(0)
}
fn write_nonblocking(&self, data: &[u8]) -> Result<usize, Error> {
Ok(data.len())
}
}
impl FileReadiness for Zero {
fn poll_read(&self, _cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
Poll::Ready(Ok(()))
}
}
#[async_trait]
impl CharDevice for Zero {
async fn read(&'static self, data: &mut [u8]) -> Result<usize, Error> {
data.fill(0);
Ok(data.len())
}
async fn write(&'static self, _data: &[u8]) -> Result<usize, Error> {
Ok(0)
}
fn read_nonblocking(&self, data: &mut [u8]) -> Result<usize, Error> {
data.fill(0);
Ok(data.len())
}
fn write_nonblocking(&self, _data: &[u8]) -> Result<usize, Error> {
Ok(0)
}
}
static NULL: Null = Null;
static ZERO: Zero = Zero;
/// Adds "pseudo"-devices to the filesystem (i.e. /dev/random)
pub fn add_pseudo_devices() -> Result<(), Error> {
let random = read_fn_node(move |_, buf| {
random::read(buf);
Ok(buf.len())
});
let root = devfs::root();
root.add_child("random", random)?;
devfs::add_named_char_device(&NULL, "null".into())?;
devfs::add_named_char_device(&ZERO, "zero".into())?;
Ok(())
}

View File

@ -18,6 +18,7 @@ use abi::{
};
use device_api::timer::RealTimeProviderDevice;
use libk::{module, random, task::thread::Thread};
use libk_device::monotonic_timestamp_provider;
use libk_mm::phys;
use crate::{device::timer::GLOBAL_TIME, fs};
@ -57,7 +58,13 @@ pub(crate) fn unmount(_options: &UnmountOptions) -> Result<(), Error> {
pub(crate) fn get_clock(ty: ClockType, value: &mut MaybeUninit<SystemTime>) -> Result<(), Error> {
let time = match ty {
ClockType::RealTime => GLOBAL_TIME.real_timestamp()?,
ClockType::Monotonic => todo!(),
ClockType::Monotonic => {
let time = monotonic_timestamp_provider().monotonic_timestamp()?;
SystemTime {
seconds: time.as_secs(),
nanoseconds: time.subsec_nanos() as _,
}
}
};
value.write(time);
Ok(())

View File

@ -5,9 +5,22 @@ use core::mem::MaybeUninit;
use abi::error::Error;
pub use abi::time::{ClockType, SystemTime};
/// Returns the "real world" time.
pub fn get_real_time() -> Result<SystemTime, Error> {
/// Returns the [SystemTime] structure representing time as described by a given [ClockType].
#[inline]
pub fn get_clock(clock_type: ClockType) -> Result<SystemTime, Error> {
let mut time = MaybeUninit::uninit();
unsafe { crate::sys::get_clock(ClockType::RealTime, &mut time) }?;
unsafe { crate::sys::get_clock(clock_type, &mut time) }?;
Ok(unsafe { time.assume_init() })
}
/// Returns the "real world" time.
#[inline]
pub fn get_real_time() -> Result<SystemTime, Error> {
get_clock(ClockType::RealTime)
}
/// Returns the monotonic time since boot.
#[inline]
pub fn get_monotonic_time() -> Result<SystemTime, Error> {
get_clock(ClockType::Monotonic)
}

View File

@ -1,7 +1,7 @@
#![feature(yggdrasil_os)]
use std::{
io::{self, Read, Seek, SeekFrom, Write},
process::ExitCode,
process::ExitCode, time::{Duration, Instant},
};
use clap::Parser;
@ -20,6 +20,8 @@ struct Args {
src_bs: u64,
#[arg(short, long, default_value_t = usize::MAX)]
count: usize,
#[arg(short)]
throughput: bool,
// TODO: remove this when pipes are a thing
#[arg(short = 'x', long)]
@ -64,27 +66,56 @@ fn dump_block(offset: u64, data: &[u8]) {
}
}
fn print_throughput(duration: Duration, bytes_read: usize) {
let read_total = humansize::format_size(bytes_read as u64, humansize::FormatSizeOptions::default());
let read_per_second = bytes_read as u64 / duration.as_secs();
let read_speed = humansize::format_size(read_per_second, humansize::FormatSizeOptions::default());
eprintln!("{read_speed}/s ({read_total} in {duration:?})");
}
fn run<I: Read + Seek, O: Write>(
mut input: I,
mut output: O,
src_position: u64,
src_block_size: u64,
mut count: usize,
as_hex: bool,
opts: &Args
) -> io::Result<()> {
const STATS_INTERVAL: Duration = Duration::from_secs(3);
let mut block = vec![0; src_block_size as usize];
let mut offset = 0;
let mut bytes_read = 0;
input.seek(SeekFrom::Start(src_position * src_block_size))?;
// input.seek(SeekFrom::Start(src_position * src_block_size))?;
let mut total_delta = Duration::ZERO;
while count != 0 {
let start = Instant::now();
let read_count = input.read(&mut block)?;
let read_time = Instant::now() - start;
bytes_read += read_count;
total_delta += read_time;
if opts.throughput && total_delta > STATS_INTERVAL {
print_throughput(total_delta, bytes_read);
total_delta = Duration::ZERO;
bytes_read = 0;
}
if read_count == 0 {
if opts.throughput {
if bytes_read != 0 {
print_throughput(total_delta, bytes_read);
}
}
break;
}
if as_hex {
if opts.as_hex {
dump_block(
(src_position + offset) * src_block_size,
&block[..read_count],
@ -127,7 +158,7 @@ fn main() -> ExitCode {
src_position,
args.src_bs,
args.count,
args.as_hex,
&args
) {
Ok(_) => ExitCode::SUCCESS,
Err(e) => {

View File

@ -9,7 +9,7 @@ use std::{
use sha2::{Digest, Sha256};
fn do_digest<P: AsRef<Path>>(path: P) -> Result<(), io::Error> {
let mut buf = [0; 4096];
let mut buf = [0; 32768];
let mut file = File::open(path)?;
let mut hasher = Sha256::new();