subkernel: pass exceptions to kernel

This commit is contained in:
mwojcik 2024-06-20 17:15:47 +08:00 committed by Sébastien Bourdeauducq
parent 4e39a7db44
commit 505ddff15d
4 changed files with 134 additions and 83 deletions

View File

@ -494,7 +494,8 @@ extern "C-unwind" fn subkernel_await_finish(id: u32, timeout: i64) {
SubkernelStatus::CommLost => raise!("SubkernelError", SubkernelStatus::CommLost => raise!("SubkernelError",
"Lost communication with satellite"), "Lost communication with satellite"),
SubkernelStatus::OtherError => raise!("SubkernelError", SubkernelStatus::OtherError => raise!("SubkernelError",
"An error occurred during subkernel operation") "An error occurred during subkernel operation"),
SubkernelStatus::Exception(e) => unsafe { crate::eh_artiq::raise(e) },
} }
}) })
} }
@ -528,7 +529,8 @@ extern "C-unwind" fn subkernel_await_message(id: i32, timeout: i64, tags: &CSlic
SubkernelStatus::CommLost => raise!("SubkernelError", SubkernelStatus::CommLost => raise!("SubkernelError",
"Lost communication with satellite"), "Lost communication with satellite"),
SubkernelStatus::OtherError => raise!("SubkernelError", SubkernelStatus::OtherError => raise!("SubkernelError",
"An error occurred during subkernel operation") "An error occurred during subkernel operation"),
SubkernelStatus::Exception(e) => unsafe { crate::eh_artiq::raise(e) },
} }
}) })
// RpcRecvRequest should be called `count` times after this to receive message data // RpcRecvRequest should be called `count` times after this to receive message data

View File

@ -11,12 +11,13 @@ pub const KERNELCPU_LAST_ADDRESS: usize = 0x4fffffff;
pub const KSUPPORT_HEADER_SIZE: usize = 0x74; pub const KSUPPORT_HEADER_SIZE: usize = 0x74;
#[derive(Debug)] #[derive(Debug)]
pub enum SubkernelStatus { pub enum SubkernelStatus<'a> {
NoError, NoError,
Timeout, Timeout,
IncorrectState, IncorrectState,
CommLost, CommLost,
OtherError OtherError,
Exception(eh::eh_artiq::Exception<'a>),
} }
#[derive(Debug)] #[derive(Debug)]
@ -106,10 +107,10 @@ pub enum Message<'a> {
SubkernelLoadRunRequest { id: u32, destination: u8, run: bool }, SubkernelLoadRunRequest { id: u32, destination: u8, run: bool },
SubkernelLoadRunReply { succeeded: bool }, SubkernelLoadRunReply { succeeded: bool },
SubkernelAwaitFinishRequest { id: u32, timeout: i64 }, SubkernelAwaitFinishRequest { id: u32, timeout: i64 },
SubkernelAwaitFinishReply { status: SubkernelStatus }, SubkernelAwaitFinishReply { status: SubkernelStatus<'a> },
SubkernelMsgSend { id: u32, destination: Option<u8>, count: u8, tag: &'a [u8], data: *const *const () }, SubkernelMsgSend { id: u32, destination: Option<u8>, count: u8, tag: &'a [u8], data: *const *const () },
SubkernelMsgRecvRequest { id: i32, timeout: i64, tags: &'a [u8] }, SubkernelMsgRecvRequest { id: i32, timeout: i64, tags: &'a [u8] },
SubkernelMsgRecvReply { status: SubkernelStatus, count: u8 }, SubkernelMsgRecvReply { status: SubkernelStatus<'a>, count: u8 },
Log(fmt::Arguments<'a>), Log(fmt::Arguments<'a>),
LogSlice(&'a str) LogSlice(&'a str)

View File

@ -95,7 +95,9 @@ pub mod subkernel {
use board_artiq::drtio_routing::RoutingTable; use board_artiq::drtio_routing::RoutingTable;
use board_misoc::clock; use board_misoc::clock;
use proto_artiq::{drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE}, rpc_proto as rpc}; use proto_artiq::{drtioaux_proto::{PayloadStatus, MASTER_PAYLOAD_MAX_SIZE}, rpc_proto as rpc};
use io::Cursor; use io::{Cursor, ProtoRead};
use eh::eh_artiq::Exception;
use cslice::CSlice;
use rtio_mgt::drtio; use rtio_mgt::drtio;
use sched::{Io, Mutex, Error as SchedError}; use sched::{Io, Mutex, Error as SchedError};
@ -226,8 +228,8 @@ pub mod subkernel {
if subkernel.state == SubkernelState::Running { if subkernel.state == SubkernelState::Running {
subkernel.state = SubkernelState::Finished { subkernel.state = SubkernelState::Finished {
status: match with_exception { status: match with_exception {
true => FinishStatus::Exception(exception_src), true => FinishStatus::Exception(exception_src),
false => FinishStatus::Ok, false => FinishStatus::Ok,
} }
} }
} }
@ -256,6 +258,42 @@ pub mod subkernel {
} }
} }
fn read_exception_string<'a>(reader: &mut Cursor<&[u8]>) -> Result<CSlice<'a, u8>, Error> {
let len = reader.read_u32()? as usize;
if len == usize::MAX {
let data = reader.read_u32()?;
Ok(unsafe { CSlice::new(data as *const u8, len) })
} else {
let pos = reader.position();
let slice = unsafe {
let ptr = reader.get_ref().as_ptr().offset(pos as isize);
CSlice::new(ptr, len)
};
reader.set_position(pos + len);
Ok(slice)
}
}
pub fn read_exception(buffer: &[u8]) -> Result<Exception, Error>
{
let mut reader = Cursor::new(buffer);
let _sync = reader.read_u32()?;
let _9 = reader.read_u8()?;
let _len = reader.read_u32()?;
// ignore the remaining exceptions, stack traces etc. - unwinding from another device would be unwise anyway
Ok(Exception {
id: reader.read_u32()?,
message: read_exception_string(&mut reader)?,
param: [reader.read_u64()? as i64, reader.read_u64()? as i64, reader.read_u64()? as i64],
file: read_exception_string(&mut reader)?,
line: reader.read_u32()?,
column: reader.read_u32()?,
function: read_exception_string(&mut reader)?
})
}
pub fn retrieve_finish_status(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, subkernel_mutex: &Mutex, pub fn retrieve_finish_status(io: &Io, aux_mutex: &Mutex, ddma_mutex: &Mutex, subkernel_mutex: &Mutex,
routing_table: &RoutingTable, id: u32) -> Result<SubkernelFinished, Error> { routing_table: &RoutingTable, id: u32) -> Result<SubkernelFinished, Error> {
let _lock = subkernel_mutex.lock(io)?; let _lock = subkernel_mutex.lock(io)?;

View File

@ -126,19 +126,6 @@ macro_rules! unexpected {
($($arg:tt)*) => (return Err(Error::Unexpected(format!($($arg)*)))); ($($arg:tt)*) => (return Err(Error::Unexpected(format!($($arg)*))));
} }
#[cfg(has_drtio)]
macro_rules! propagate_subkernel_exception {
( $exception:ident, $stream:ident ) => {
error!("Exception in subkernel");
match $stream {
None => return Ok(true),
Some(ref mut $stream) => {
$stream.write_all($exception)?;
}
}
}
}
// Persistent state // Persistent state
#[derive(Debug)] #[derive(Debug)]
struct Congress { struct Congress {
@ -690,10 +677,13 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
Ok(ref res) => { Ok(ref res) => {
if res.comm_lost { if res.comm_lost {
kern::SubkernelStatus::CommLost kern::SubkernelStatus::CommLost
} else if let Some(exception) = &res.exception { } else if let Some(raw_exception) = &res.exception {
propagate_subkernel_exception!(exception, stream); let exception = subkernel::read_exception(raw_exception);
// will not be called after exception is served if let Ok(exception) = exception {
kern::SubkernelStatus::OtherError kern::SubkernelStatus::Exception(exception)
} else {
kern::SubkernelStatus::OtherError
}
} else { } else {
kern::SubkernelStatus::NoError kern::SubkernelStatus::NoError
} }
@ -712,72 +702,92 @@ fn process_kern_message(io: &Io, aux_mutex: &Mutex,
#[cfg(has_drtio)] #[cfg(has_drtio)]
&kern::SubkernelMsgRecvRequest { id, timeout, tags } => { &kern::SubkernelMsgRecvRequest { id, timeout, tags } => {
let message_received = subkernel::message_await(io, subkernel_mutex, id as u32, timeout); let message_received = subkernel::message_await(io, subkernel_mutex, id as u32, timeout);
let (status, count) = match message_received { if let Err(SubkernelError::SubkernelFinished) = message_received {
Ok(ref message) => (kern::SubkernelStatus::NoError, message.count), let res = subkernel::retrieve_finish_status(io, aux_mutex, ddma_mutex, subkernel_mutex,
Err(SubkernelError::Timeout) => (kern::SubkernelStatus::Timeout, 0), routing_table, id as u32)?;
Err(SubkernelError::IncorrectState) => (kern::SubkernelStatus::IncorrectState, 0), if res.comm_lost {
Err(SubkernelError::SubkernelFinished) => { kern_send(io,
let res = subkernel::retrieve_finish_status(io, aux_mutex, ddma_mutex, subkernel_mutex, &kern::SubkernelMsgRecvReply {
routing_table, id as u32)?; status: kern::SubkernelStatus::CommLost,
if res.comm_lost { count: 0
(kern::SubkernelStatus::CommLost, 0)
} else if let Some(exception) = &res.exception {
propagate_subkernel_exception!(exception, stream);
(kern::SubkernelStatus::OtherError, 0)
} else {
(kern::SubkernelStatus::OtherError, 0)
}
}
Err(_) => (kern::SubkernelStatus::OtherError, 0)
};
kern_send(io, &kern::SubkernelMsgRecvReply { status: status, count: count})?;
if let Ok(message) = message_received {
// receive code almost identical to RPC recv, except we are not reading from a stream
let mut reader = Cursor::new(message.data);
let mut current_tags = tags;
let mut i = 0;
loop {
// kernel has to consume all arguments in the whole message
let slot = kern_recv(io, |reply| {
match reply {
&kern::RpcRecvRequest(slot) => Ok(slot),
other => unexpected!(
"expected root value slot from kernel CPU, not {:?}", other)
}
})?; })?;
let res = rpc::recv_return(&mut reader, current_tags, slot, &|size| -> Result<_, Error<SchedError>> { } else if let Some(raw_exception) = &res.exception {
if size == 0 { let exception = subkernel::read_exception(raw_exception);
return Ok(0 as *mut ()) if let Ok(exception) = exception {
} kern_send(io,
kern_send(io, &kern::RpcRecvReply(Ok(size)))?; &kern::SubkernelMsgRecvReply {
Ok(kern_recv(io, |reply| { status: kern::SubkernelStatus::Exception(exception),
count: 0
})?;
} else {
kern_send(io,
&kern::SubkernelMsgRecvReply {
status: kern::SubkernelStatus::OtherError,
count: 0
})?;
}
} else {
kern_send(io,
&kern::SubkernelMsgRecvReply {
status: kern::SubkernelStatus::OtherError,
count: 0
})?;
}
} else {
let (status, count) = match message_received {
Ok(ref message) => (kern::SubkernelStatus::NoError, message.count),
Err(SubkernelError::Timeout) => (kern::SubkernelStatus::Timeout, 0),
Err(SubkernelError::IncorrectState) => (kern::SubkernelStatus::IncorrectState, 0),
Err(SubkernelError::SubkernelFinished) => unreachable!(), // taken care of above
Err(_) => (kern::SubkernelStatus::OtherError, 0)
};
kern_send(io, &kern::SubkernelMsgRecvReply { status: status, count: count})?;
if let Ok(message) = message_received {
// receive code almost identical to RPC recv, except we are not reading from a stream
let mut reader = Cursor::new(message.data);
let mut current_tags = tags;
let mut i = 0;
loop {
// kernel has to consume all arguments in the whole message
let slot = kern_recv(io, |reply| {
match reply { match reply {
&kern::RpcRecvRequest(slot) => Ok(slot), &kern::RpcRecvRequest(slot) => Ok(slot),
other => unexpected!( other => unexpected!(
"expected nested value slot from kernel CPU, not {:?}", other) "expected root value slot from kernel CPU, not {:?}", other)
} }
})?) })?;
}); let res = rpc::recv_return(&mut reader, current_tags, slot, &|size| -> Result<_, Error<SchedError>> {
match res { if size == 0 {
Ok(new_tags) => { return Ok(0 as *mut ())
kern_send(io, &kern::RpcRecvReply(Ok(0)))?;
i += 1;
if i < message.count {
// update the tag for next read
current_tags = new_tags;
} else {
// should be done by then
break;
} }
}, kern_send(io, &kern::RpcRecvReply(Ok(size)))?;
Err(_) => unexpected!("expected valid subkernel message data") Ok(kern_recv(io, |reply| {
}; match reply {
&kern::RpcRecvRequest(slot) => Ok(slot),
other => unexpected!(
"expected nested value slot from kernel CPU, not {:?}", other)
}
})?)
});
match res {
Ok(new_tags) => {
kern_send(io, &kern::RpcRecvReply(Ok(0)))?;
i += 1;
if i < message.count {
// update the tag for next read
current_tags = new_tags;
} else {
// should be done by then
break;
}
},
Err(_) => unexpected!("expected valid subkernel message data")
};
}
} }
Ok(())
} else {
// if timed out, no data has been received, exception should be raised by kernel // if timed out, no data has been received, exception should be raised by kernel
Ok(())
} }
Ok(())
}, },
request => unexpected!("unexpected request {:?} from kernel CPU", request) request => unexpected!("unexpected request {:?} from kernel CPU", request)