task: better async error handling

This commit is contained in:
Mark Poliakov 2023-12-08 23:19:12 +02:00
parent 2770281213
commit f166968e57
5 changed files with 68 additions and 35 deletions

15
src/device/nvme/error.rs Normal file
View File

@ -0,0 +1,15 @@
use abi::error::Error;
use super::queue::CommandError;
#[derive(Debug)]
pub enum NvmeError {
MemoryError(Error),
CommandError(CommandError),
}
impl From<CommandError> for NvmeError {
fn from(value: CommandError) -> Self {
Self::CommandError(value)
}
}

View File

@ -29,12 +29,14 @@ use crate::{
use self::{ use self::{
command::{CreateIoCompletionQueue, CreateIoSubmissionQueue, SetFeatureRequest}, command::{CreateIoCompletionQueue, CreateIoSubmissionQueue, SetFeatureRequest},
error::NvmeError,
queue::QueuePair, queue::QueuePair,
}; };
use super::bus::pci::{FromPciBus, PciDeviceInfo}; use super::bus::pci::{FromPciBus, PciDeviceInfo};
mod command; mod command;
mod error;
mod queue; mod queue;
register_bitfields! { register_bitfields! {
@ -120,29 +122,23 @@ impl Regs {
} }
impl NvmeController { impl NvmeController {
async fn late_init(&'static self) { async fn late_init(&'static self) -> Result<(), NvmeError> {
// let ioq = QueuePair::new(capacity, sq_doorbell, cq_doorbell).unwrap();
runtime::spawn(self.poll_task()).expect("Couldn't spawn NVMe poll task"); runtime::spawn(self.poll_task()).expect("Couldn't spawn NVMe poll task");
let admin_q = self.admin_q.get(); let admin_q = self.admin_q.get();
// Request a CQ/SQ pair for I/O // Request a CQ/SQ pair for I/O
admin_q admin_q
.request_no_data(SetFeatureRequest::NumberOfQueues(1, 1)) .request_no_data(SetFeatureRequest::NumberOfQueues(1, 1))
.unwrap() .await?;
.await
.unwrap();
// Allocate the queue // Allocate the queue
let (sq_doorbell, cq_doorbell) = unsafe { self.doorbell_pair(1) }; let (sq_doorbell, cq_doorbell) = unsafe { self.doorbell_pair(1) };
let io_q = QueuePair::new(32, sq_doorbell, cq_doorbell).unwrap(); let io_q = QueuePair::new(32, sq_doorbell, cq_doorbell).map_err(NvmeError::MemoryError)?;
// Identify the controller // Identify the controller
let identify = admin_q let identify = admin_q
.request(IdentifyControllerRequest { nsid: 0 }) .request(IdentifyControllerRequest { nsid: 0 })?
.unwrap() .await?;
.await
.unwrap();
// Create the queue on the device side // Create the queue on the device side
admin_q admin_q
@ -151,9 +147,7 @@ impl NvmeController {
size: 32, size: 32,
data: io_q.cq_physical_pointer(), data: io_q.cq_physical_pointer(),
}) })
.unwrap() .await?;
.await
.unwrap();
admin_q admin_q
.request_no_data(CreateIoSubmissionQueue { .request_no_data(CreateIoSubmissionQueue {
id: 1, id: 1,
@ -161,9 +155,7 @@ impl NvmeController {
size: 32, size: 32,
data: io_q.sq_physical_pointer(), data: io_q.sq_physical_pointer(),
}) })
.unwrap() .await?;
.await
.unwrap();
loop {} loop {}
} }

View File

@ -22,7 +22,10 @@ use crate::{
task::runtime::QueueWaker, task::runtime::QueueWaker,
}; };
use super::command::{Command, Request}; use super::{
command::{Command, Request},
error::NvmeError,
};
#[derive(Zeroable, Pod, Clone, Copy, Debug)] #[derive(Zeroable, Pod, Clone, Copy, Debug)]
#[repr(C)] #[repr(C)]
@ -338,12 +341,7 @@ impl<'a> QueuePair<'a> {
} }
} }
pub fn submit<C: Command>( pub fn submit<C: Command>(&self, cmd: C, ranges: &[PhysicalAddress], set_pending: bool) -> u32 {
&self,
cmd: C,
ranges: &[PhysicalAddress],
set_pending: bool,
) -> Result<u32, Error> {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
let mut sqe = SubmissionQueueEntry::zeroed(); let mut sqe = SubmissionQueueEntry::zeroed();
@ -370,24 +368,27 @@ impl<'a> QueuePair<'a> {
inner.sq.enqueue(sqe); inner.sq.enqueue(sqe);
Ok(command_id) command_id
} }
pub fn request_no_data<'r, C: Command>( pub fn request_no_data<'r, C: Command>(
&'r self, &'r self,
req: C, req: C,
) -> Result<impl Future<Output = Result<(), CommandError>> + 'r, Error> ) -> impl Future<Output = Result<(), CommandError>> + 'r
where where
'r: 'a, 'r: 'a,
{ {
let command_id = self.submit(req, &[], true)?; let command_id = self.submit(req, &[], true);
Ok(self.wait_for_completion(command_id, ())) self.wait_for_completion(command_id, ())
} }
pub fn request<'r, R: Request>( pub fn request<'r, R: Request>(
&'r self, &'r self,
req: R, req: R,
) -> Result<impl Future<Output = Result<PhysicalRefMut<'r, R::Response>, CommandError>>, Error> ) -> Result<
impl Future<Output = Result<PhysicalRefMut<'r, R::Response>, CommandError>>,
NvmeError,
>
where where
R::Response: 'r, R::Response: 'r,
'r: 'a, 'r: 'a,
@ -395,11 +396,11 @@ impl<'a> QueuePair<'a> {
assert_ne!(size_of::<R::Response>(), 0); assert_ne!(size_of::<R::Response>(), 0);
assert!(size_of::<R::Response>() < 0x1000); assert!(size_of::<R::Response>() < 0x1000);
let page = phys::alloc_page()?; let page = phys::alloc_page().map_err(NvmeError::MemoryError)?;
// TODO PageBox // TODO PageBox
let response = unsafe { PhysicalRefMut::map(page) }; let response = unsafe { PhysicalRefMut::map(page) };
let command_id = self.submit(req, &[page], true)?; let command_id = self.submit(req, &[page], true);
Ok(self.wait_for_completion(command_id, response)) Ok(self.wait_for_completion(command_id, response))
} }

View File

@ -7,7 +7,7 @@ use futures_util::{task::waker_ref, Future};
use crate::task::{spawn_kernel_closure, thread::Thread}; use crate::task::{spawn_kernel_closure, thread::Thread};
use super::{ use super::{
task::Task, task::{Task, Termination},
task_queue::{self}, task_queue::{self},
}; };
@ -38,7 +38,9 @@ pub fn spawn_async_worker(index: usize) -> Result<(), Error> {
} }
/// Creates a new task for the [Future] and queues it for execution in background /// Creates a new task for the [Future] and queues it for execution in background
pub fn spawn<F: Future<Output = ()> + Send + 'static>(future: F) -> Result<(), Error> { pub fn spawn<T: Termination, F: Future<Output = T> + Send + 'static>(
future: F,
) -> Result<(), Error> {
enqueue(Task::new(future)) enqueue(Task::new(future))
} }

View File

@ -1,9 +1,15 @@
use core::fmt;
use alloc::sync::Arc; use alloc::sync::Arc;
use futures_util::{future::BoxFuture, task::ArcWake, Future, FutureExt}; use futures_util::{future::BoxFuture, task::ArcWake, Future, FutureExt};
use kernel_util::sync::IrqSafeSpinlock; use kernel_util::sync::IrqSafeSpinlock;
use super::executor; use super::executor;
pub trait Termination {
fn print(&self);
}
pub struct Task { pub struct Task {
pub(super) future: IrqSafeSpinlock<Option<BoxFuture<'static, ()>>>, pub(super) future: IrqSafeSpinlock<Option<BoxFuture<'static, ()>>>,
} }
@ -15,8 +21,25 @@ impl ArcWake for Task {
} }
impl Task { impl Task {
pub fn new<F: Future<Output = ()> + Send + 'static>(future: F) -> Arc<Self> { pub fn new<T: Termination, F: Future<Output = T> + Send + 'static>(future: F) -> Arc<Self> {
let future = IrqSafeSpinlock::new(Some(future.boxed())); let future = IrqSafeSpinlock::new(Some(
async move {
future.await.print();
}
.boxed(),
));
Arc::new(Self { future }) Arc::new(Self { future })
} }
} }
impl Termination for () {
fn print(&self) {}
}
impl<T, E: fmt::Debug> Termination for Result<T, E> {
fn print(&self) {
if let Err(error) = self {
errorln!("A task finished with an error: {:?}", error);
}
}
}