feat(kernel): lazy user FPU: off until first FP insn (illegal-insn trap -> zeroed regs, retry), save on dirty trap, restore on switch, off for threads without + kernel; spawntest per-thread FP check
This commit is contained in:
@@ -173,6 +173,57 @@ fn encrypted_home(fs: &mut Storage, home_fs: &mut Storage) -> Result<(), &'stati
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Floating-point work that only comes out right if nobody else's
|
||||
/// registers get in: a sum over `ROUNDS`, yielding (so switching threads)
|
||||
/// all the way.
|
||||
fn float_work(step: f64) -> u64 {
|
||||
const ROUNDS: u32 = 2000;
|
||||
let mut sum = 0.0f64;
|
||||
let mut x = core::hint::black_box(step);
|
||||
for _ in 0..ROUNDS {
|
||||
sum += x * 0.5;
|
||||
x += step;
|
||||
syscall::yield_now();
|
||||
}
|
||||
sum.to_bits()
|
||||
}
|
||||
|
||||
/// What `float_work(step)` gives when nothing interferes.
|
||||
fn float_expected(step: f64) -> u64 {
|
||||
let mut sum = 0.0f64;
|
||||
let mut x = step;
|
||||
for _ in 0..2000 {
|
||||
sum += x * 0.5;
|
||||
x += step;
|
||||
}
|
||||
sum.to_bits()
|
||||
}
|
||||
|
||||
/// The other thread's answer, once it has one.
|
||||
static FLOAT_RESULT: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(0);
|
||||
|
||||
fn float_thread(_: &'static core::sync::atomic::AtomicU64) -> ! {
|
||||
FLOAT_RESULT.store(float_work(0.25), core::sync::atomic::Ordering::Release);
|
||||
syscall::exit_thread()
|
||||
}
|
||||
|
||||
/// Two threads computing with floats at once each get their own registers
|
||||
/// (the kernel saves and restores them on every switch).
|
||||
fn floating_point_is_kept_per_thread() -> Result<(), &'static str> {
|
||||
if !libkos::thread::spawn(&FLOAT_RESULT, float_thread) {
|
||||
return Err("cannot start a thread");
|
||||
}
|
||||
let mine = float_work(1.75);
|
||||
while FLOAT_RESULT.load(core::sync::atomic::Ordering::Acquire) == 0 {
|
||||
syscall::yield_now();
|
||||
}
|
||||
let theirs = FLOAT_RESULT.load(core::sync::atomic::Ordering::Acquire);
|
||||
if mine != float_expected(1.75) || theirs != float_expected(0.25) {
|
||||
return Err("floating-point registers leaked between threads");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// How many requests the `flaky` answering now has served.
|
||||
fn flaky() -> Result<usize, &'static str> {
|
||||
syscall::request(FLAKY, [flaky::op::WHO, 0, 0, 0]).map(|r| r[0]).map_err(|_| "flaky does not answer")
|
||||
@@ -246,6 +297,9 @@ fn check() -> Result<(), &'static str> {
|
||||
revoke_stops_a_running_app(died)?;
|
||||
libkos::println!("spawntest: revoking stops a running app, allowing lets it run: ok");
|
||||
|
||||
floating_point_is_kept_per_thread()?;
|
||||
libkos::println!("spawntest: floating point is kept per thread: ok");
|
||||
|
||||
restarted_after_crashes()?;
|
||||
libkos::println!("spawntest: a crashed server is restarted, a crash loop backs off: ok");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user