140 lines
5.0 KiB
Rust
140 lines
5.0 KiB
Rust
//! What init prints never waits on the log server: init restarts the log
|
|
//! server, so a blocking call to it could wait on itself. Text goes into a
|
|
//! ring; a thread of init's own takes it to the log and waits there when
|
|
//! the log is down, instead of init. Until that thread runs (early boot),
|
|
//! init prints to the console directly. The same thread carries the few
|
|
//! messages init sends other servers it restarts (`post`).
|
|
|
|
use core::fmt;
|
|
use core::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
|
|
use libkos::syscall;
|
|
use types::bounded::CapSlot;
|
|
|
|
const SIZE: usize = 4096;
|
|
/// Bytes waiting: written at `TAIL` by init's thread, read at `HEAD` by
|
|
/// the printer.
|
|
static RING: [AtomicU8; SIZE] = [const { AtomicU8::new(0) }; SIZE];
|
|
static HEAD: AtomicUsize = AtomicUsize::new(0);
|
|
static TAIL: AtomicUsize = AtomicUsize::new(0);
|
|
/// Bytes dropped because the ring was full: the log was down a long time.
|
|
static LOST: AtomicUsize = AtomicUsize::new(0);
|
|
/// The printer's notification, once it runs.
|
|
static WAKE: AtomicUsize = AtomicUsize::new(usize::MAX);
|
|
|
|
/// Messages waiting: an endpoint slot and four words each.
|
|
const POSTS: usize = 16;
|
|
static POSTED: [[AtomicUsize; 5]; POSTS] = [const { [const { AtomicUsize::new(0) }; 5] }; POSTS];
|
|
static POST_HEAD: AtomicUsize = AtomicUsize::new(0);
|
|
static POST_TAIL: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
/// Has `words` called to `ep` by the printer thread, so init never waits
|
|
/// on it; dropped (and said so) if too many are waiting.
|
|
pub(crate) fn post(ep: CapSlot, words: [usize; 4]) {
|
|
let tail = POST_TAIL.load(Ordering::Relaxed);
|
|
if tail - POST_HEAD.load(Ordering::Acquire) == POSTS || wake().is_none() {
|
|
crate::outln!("init: a message to a server was dropped");
|
|
return;
|
|
}
|
|
let slot = &POSTED[tail % POSTS];
|
|
slot[0].store(ep.value() as usize, Ordering::Relaxed);
|
|
for (w, v) in slot[1..].iter().zip(words) {
|
|
w.store(v, Ordering::Relaxed);
|
|
}
|
|
POST_TAIL.store(tail + 1, Ordering::Release);
|
|
if let Some(wake) = wake() {
|
|
let _ = syscall::signal(wake);
|
|
}
|
|
}
|
|
|
|
fn wake() -> Option<CapSlot> {
|
|
u8::try_from(WAKE.load(Ordering::Acquire)).ok().and_then(CapSlot::new)
|
|
}
|
|
|
|
pub(crate) fn write(bytes: &[u8]) {
|
|
let Some(wake) = wake() else {
|
|
libkos::io::write_bytes(bytes);
|
|
return;
|
|
};
|
|
let tail = TAIL.load(Ordering::Relaxed);
|
|
let room = SIZE - (tail - HEAD.load(Ordering::Acquire));
|
|
let n = bytes.len().min(room);
|
|
for (i, b) in bytes[..n].iter().enumerate() {
|
|
RING[(tail + i) % SIZE].store(*b, Ordering::Relaxed);
|
|
}
|
|
TAIL.store(tail + n, Ordering::Release);
|
|
LOST.fetch_add(bytes.len() - n, Ordering::Relaxed);
|
|
let _ = syscall::signal(wake);
|
|
}
|
|
|
|
struct Ring;
|
|
|
|
impl fmt::Write for Ring {
|
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
write(s.as_bytes());
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub(crate) fn print(args: fmt::Arguments) {
|
|
let _ = fmt::Write::write_fmt(&mut Ring, args);
|
|
}
|
|
|
|
/// Starts the printer; from here on init's output goes through the ring.
|
|
pub(crate) fn start() -> bool {
|
|
let Ok(wake) = syscall::create_notification() else { return false };
|
|
WAKE.store(wake.value() as usize, Ordering::Release);
|
|
if libkos::thread::spawn(&WAKE, printer) {
|
|
return true;
|
|
}
|
|
WAKE.store(usize::MAX, Ordering::Release);
|
|
false
|
|
}
|
|
|
|
fn printer(_: &'static AtomicUsize) -> ! {
|
|
let mut chunk = [0u8; 256];
|
|
loop {
|
|
if let Some(wake) = wake() {
|
|
let _ = syscall::wait(wake);
|
|
}
|
|
loop {
|
|
let (head, tail) = (HEAD.load(Ordering::Relaxed), TAIL.load(Ordering::Acquire));
|
|
if head == tail {
|
|
break;
|
|
}
|
|
let n = (tail - head).min(chunk.len());
|
|
for (i, b) in chunk[..n].iter_mut().enumerate() {
|
|
*b = RING[(head + i) % SIZE].load(Ordering::Relaxed);
|
|
}
|
|
HEAD.store(head + n, Ordering::Release);
|
|
// Blocks while the log server is down; init goes on.
|
|
libkos::io::write_bytes(&chunk[..n]);
|
|
}
|
|
let lost = LOST.swap(0, Ordering::Relaxed);
|
|
if lost > 0 {
|
|
libkos::println!("init: {} bytes of output lost while the log was down", lost);
|
|
}
|
|
while POST_HEAD.load(Ordering::Relaxed) != POST_TAIL.load(Ordering::Acquire) {
|
|
let head = POST_HEAD.load(Ordering::Relaxed);
|
|
let slot = &POSTED[head % POSTS];
|
|
let words = core::array::from_fn(|i| slot[i + 1].load(Ordering::Relaxed));
|
|
let ep = u8::try_from(slot[0].load(Ordering::Relaxed)).ok().and_then(CapSlot::new);
|
|
POST_HEAD.store(head + 1, Ordering::Release);
|
|
if let Some(ep) = ep {
|
|
// Waits while that server is down; init goes on.
|
|
let _ = syscall::request(ep, words);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! out {
|
|
($($arg:tt)*) => ($crate::output::print(format_args!($($arg)*)));
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! outln {
|
|
() => ($crate::out!("\n"));
|
|
($($arg:tt)*) => ($crate::out!("{}\n", format_args!($($arg)*)));
|
|
}
|