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

View File

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

View File

@ -7,7 +7,7 @@ use futures_util::{task::waker_ref, Future};
use crate::task::{spawn_kernel_closure, thread::Thread};
use super::{
task::Task,
task::{Task, Termination},
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
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))
}

View File

@ -1,9 +1,15 @@
use core::fmt;
use alloc::sync::Arc;
use futures_util::{future::BoxFuture, task::ArcWake, Future, FutureExt};
use kernel_util::sync::IrqSafeSpinlock;
use super::executor;
pub trait Termination {
fn print(&self);
}
pub struct Task {
pub(super) future: IrqSafeSpinlock<Option<BoxFuture<'static, ()>>>,
}
@ -15,8 +21,25 @@ impl ArcWake for Task {
}
impl Task {
pub fn new<F: Future<Output = ()> + Send + 'static>(future: F) -> Arc<Self> {
let future = IrqSafeSpinlock::new(Some(future.boxed()));
pub fn new<T: Termination, F: Future<Output = T> + Send + 'static>(future: F) -> Arc<Self> {
let future = IrqSafeSpinlock::new(Some(
async move {
future.await.print();
}
.boxed(),
));
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);
}
}
}