Skip to content

Commit

Permalink
kernel: move DEBUG_OUT_V0 interface to per-thread buffering
Browse files Browse the repository at this point in the history
  • Loading branch information
Qix- committed Jan 22, 2025
1 parent f20eac8 commit 1eb482e
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 29 deletions.
61 changes: 34 additions & 27 deletions oro-kernel/src/iface/root_ring/debug_out_v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,45 +16,46 @@
use core::marker::PhantomData;

use oro_mem::alloc::vec::Vec;
use oro_sync::{Lock, Mutex};
use oro_sysabi::{key, syscall::Error as SysError};

use crate::{
arch::Arch, interface::Interface, syscall::InterfaceResponse, tab::Tab, thread::Thread,
};

/// The default line buffer size.
const DEFAULT_LINE_BUFFER: usize = 256;
/// The hard-coded maximum line buffer size.
const HARD_MAXIMUM: u64 = 1024;
/// The hard-coded minimum line buffer size.
const HARD_MINIMUM: u64 = 1;

/// Inner state of the debug output stream.
struct Inner {
struct BufferState {
/// The number of bytes to line buffer before
/// forcing a flush.
line_buffer: usize,
/// Buffer for line buffering.
buffer: Vec<u8>,
}

impl Default for Inner {
impl Default for BufferState {
fn default() -> Self {
Self {
line_buffer: 256,
line_buffer: DEFAULT_LINE_BUFFER,
buffer: Vec::new(),
}
}
}

/// See the module level documentation for information about
/// the debug output stream interface.
pub struct DebugOutV0<A: Arch>(Mutex<Inner>, PhantomData<A>);
pub struct DebugOutV0<A: Arch>(PhantomData<A>);

impl<A: Arch> DebugOutV0<A> {
/// Creates a new `DebugOutV0` instance.
#[must_use]
pub fn new() -> Self {
Self(Mutex::new(Inner::default()), PhantomData)
Self(PhantomData)
}
}

Expand All @@ -63,55 +64,61 @@ impl<A: Arch> Interface<A> for DebugOutV0<A> {
oro_sysabi::id::iface::ROOT_DEBUG_OUT_V0
}

fn get(&self, _thread: &Tab<Thread<A>>, index: u64, key: u64) -> InterfaceResponse {
fn get(&self, thread: &Tab<Thread<A>>, index: u64, key: u64) -> InterfaceResponse {
if index != 0 {
return InterfaceResponse::immediate(SysError::BadIndex, 0);
}

match key {
key!("write") => InterfaceResponse::immediate(SysError::WriteOnly, 0),
key!("line_max") => InterfaceResponse::ok(self.0.lock().line_buffer as u64),
key!("line_max") => {
InterfaceResponse::ok(thread.with(|t| {
t.data()
.try_get::<BufferState>()
.map_or_else(|| DEFAULT_LINE_BUFFER, |b| b.line_buffer) as u64
}))
}
key!("hard_max") => InterfaceResponse::ok(HARD_MAXIMUM),
key!("hard_min") => InterfaceResponse::ok(HARD_MINIMUM),
_ => InterfaceResponse::immediate(SysError::BadKey, 0),
}
}

fn set(&self, _thread: &Tab<Thread<A>>, index: u64, key: u64, value: u64) -> InterfaceResponse {
fn set(&self, thread: &Tab<Thread<A>>, index: u64, key: u64, value: u64) -> InterfaceResponse {
if index != 0 {
return InterfaceResponse::immediate(SysError::BadIndex, 0);
}

match key {
key!("line_max") => {
let value = value.clamp(HARD_MINIMUM, HARD_MAXIMUM);

self.0.lock().line_buffer = value as usize;

thread.with_mut(|t| t.data_mut().get::<BufferState>().line_buffer = value as usize);
InterfaceResponse::ok(0)
}
key!("write") => {
// The value itself holds the bytes; we consume each of the 8 bytes
// from `value` until we encounter a `0` byte, after which we ignore
// the rest.
let bytes = value.to_be_bytes();
let mut inner = self.0.lock();

for b in bytes {
let flush = match b {
0 => continue,
b'\n' => true,
_ => {
inner.buffer.push(b);
inner.buffer.len() >= inner.line_buffer
thread.with_mut(|t| {
let inner = t.data_mut().get::<BufferState>();

for b in bytes {
let flush = match b {
0 => continue,
b'\n' => true,
_ => {
inner.buffer.push(b);
inner.buffer.len() >= inner.line_buffer
}
};

if flush {
::oro_debug::log_debug_bytes(&inner.buffer);
inner.buffer.clear();
}
};

if flush {
::oro_debug::log_debug_bytes(&inner.buffer);
inner.buffer.clear();
}
}
});

InterfaceResponse::ok(0)
}
Expand Down
17 changes: 16 additions & 1 deletion oro-kernel/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
module::Module,
ring::Ring,
tab::Tab,
table::Table,
table::{Table, TypeTable},
thread::Thread,
};

Expand Down Expand Up @@ -55,6 +55,8 @@ pub struct Instance<A: Arch> {
pub(super) threads: Table<Tab<Thread<A>>>,
/// The instance's architecture handle.
handle: A::InstanceHandle,
/// The instance's associated data.
data: TypeTable,
}

impl<A: Arch> Instance<A> {
Expand Down Expand Up @@ -84,6 +86,7 @@ impl<A: Arch> Instance<A> {
ring: ring.clone(),
threads: Table::new(),
handle,
data: TypeTable::new(),
})
.ok_or(MapError::OutOfMemory)?;

Expand Down Expand Up @@ -112,4 +115,16 @@ impl<A: Arch> Instance<A> {
pub fn mapper(&self) -> &UserHandle<A> {
self.handle.mapper()
}

/// Returns a reference to the instance's data.
#[inline]
pub fn data(&self) -> &TypeTable {
&self.data
}

/// Returns a mutable reference to the instance's data.
#[inline]
pub fn data_mut(&mut self) -> &mut TypeTable {
&mut self.data
}
}
18 changes: 17 additions & 1 deletion oro-kernel/src/ring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
instance::Instance,
interface::{Interface, RingInterface},
tab::Tab,
table::Table,
table::{Table, TypeTable},
};

/// A singular ring.
Expand Down Expand Up @@ -46,6 +46,8 @@ pub struct Ring<A: Arch> {
children: Vec<Tab<Ring<A>>>,
/// The interfaces exposed to the ring, grouped by type.
interfaces_by_type: Table<Vec<Tab<RingInterface<A>>>>,
/// Associated ring data.
data: TypeTable,
}

impl<A: Arch> Ring<A> {
Expand All @@ -63,6 +65,7 @@ impl<A: Arch> Ring<A> {
mapper,
children: Vec::new(),
interfaces_by_type: Table::new(),
data: TypeTable::new(),
})
.ok_or(MapError::OutOfMemory)?;

Expand Down Expand Up @@ -105,6 +108,7 @@ impl<A: Arch> Ring<A> {
mapper,
children: Vec::new(),
interfaces_by_type: Table::new(),
data: TypeTable::new(),
})
.ok_or(MapError::OutOfMemory)
}
Expand Down Expand Up @@ -160,4 +164,16 @@ impl<A: Arch> Ring<A> {
.push(tab.clone());
Some(tab)
}

/// Returns a reference to the ring's data.
#[must_use]
pub fn data(&self) -> &TypeTable {
&self.data
}

/// Returns a mutable reference to the ring's data.
#[must_use]
pub fn data_mut(&mut self) -> &mut TypeTable {
&mut self.data
}
}
16 changes: 16 additions & 0 deletions oro-kernel/src/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use crate::{
ring::Ring,
syscall::{InFlightState, InFlightSystemCall, InFlightSystemCallHandle, SystemCallResponse},
tab::Tab,
table::TypeTable,
};

/// A thread's run state.
Expand Down Expand Up @@ -82,6 +83,8 @@ pub struct Thread<A: Arch> {
/// If `Some`, another thread has requested a `run_state`
/// change and should be notified when it occurs.
run_state_transition: Option<(RunState, InFlightSystemCall)>,
/// Associated thread data.
data: TypeTable,
}

impl<A: Arch> Thread<A> {
Expand Down Expand Up @@ -185,6 +188,7 @@ impl<A: Arch> Thread<A> {
state: State::default(),
run_state: RunState::Running,
run_state_transition: None,
data: TypeTable::new(),
};

let tab = crate::tab::get().add(this).ok_or(MapError::OutOfMemory)?;
Expand Down Expand Up @@ -423,6 +427,18 @@ impl<A: Arch> Thread<A> {
panic!("thread is not running on the given core");
}
}

/// Returns a reference to the thread's data table.
#[inline]
pub fn data(&self) -> &TypeTable {
&self.data
}

/// Returns a mutable reference to the thread's data table.
#[inline]
pub fn data_mut(&mut self) -> &mut TypeTable {
&mut self.data
}
}

/// Error type for thread scheduling.
Expand Down

0 comments on commit 1eb482e

Please sign in to comment.