feat(kernel): add handle_tick and handle_interrupt entry points
This commit is contained in:
@@ -1,15 +1,63 @@
|
|||||||
use domain::scheduler::SchedulerDecision;
|
use domain::scheduler::{Scheduler, SchedulerDecision};
|
||||||
use platform::Platform;
|
use platform::Platform;
|
||||||
use types::id::{CoreId, IrqId, ThreadId};
|
use types::id::{CoreId, IrqId, ThreadId};
|
||||||
|
|
||||||
use crate::Kernel;
|
use crate::Kernel;
|
||||||
|
|
||||||
impl<P: Platform> Kernel<P> {
|
impl<P: Platform> Kernel<P> {
|
||||||
pub fn handle_tick(&mut self, _core: CoreId) -> SchedulerDecision {
|
pub fn handle_tick(&mut self, core: CoreId) -> SchedulerDecision {
|
||||||
SchedulerDecision::Continue
|
let current_tid = match self.scheduler.current(core) {
|
||||||
|
Some(tid) => tid,
|
||||||
|
None => return SchedulerDecision::Continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let thread = match self.threads.get_mut(current_tid) {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(_) => return SchedulerDecision::Continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let expired = thread.time_slice_mut().tick();
|
||||||
|
if expired {
|
||||||
|
thread.time_slice_mut().reset();
|
||||||
|
let priority = thread.priority();
|
||||||
|
if thread.preempt().is_err() {
|
||||||
|
return SchedulerDecision::Continue;
|
||||||
|
}
|
||||||
|
self.scheduler.add(current_tid, priority, core);
|
||||||
|
match self.scheduler.next(core) {
|
||||||
|
Some(next) => {
|
||||||
|
if self.threads.get_mut(next).and_then(|t| t.set_running(core)).is_ok() {
|
||||||
|
SchedulerDecision::Preempt(next)
|
||||||
|
} else {
|
||||||
|
let _ = self.threads.get_mut(current_tid).map(|t| t.set_running(core));
|
||||||
|
SchedulerDecision::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let _ = self.threads.get_mut(current_tid).map(|t| t.set_running(core));
|
||||||
|
SchedulerDecision::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.scheduler.tick(core, current_tid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_interrupt(&mut self, _irq: IrqId) -> Option<ThreadId> {
|
pub fn handle_interrupt(&mut self, irq: IrqId) -> Option<ThreadId> {
|
||||||
None
|
self.platform.mask_irq(irq);
|
||||||
|
|
||||||
|
let tid = self.irqs.owner(irq)?;
|
||||||
|
let thread = self.threads.get_mut(tid).ok()?;
|
||||||
|
|
||||||
|
if !matches!(thread.state(), domain::thread::ThreadState::Blocked(_)) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let priority = thread.priority();
|
||||||
|
let core = self.platform.core_id();
|
||||||
|
let _ = thread.unblock();
|
||||||
|
self.scheduler.add(tid, priority, core);
|
||||||
|
|
||||||
|
Some(tid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,4 +152,41 @@ impl<P: Platform> Kernel<P> {
|
|||||||
let cap = Capability::new(resource, permissions, Generation::initial());
|
let cap = Capability::new(resource, permissions, Generation::initial());
|
||||||
self.processes.get_mut(to)?.capabilities_mut().insert(cap)
|
self.processes.get_mut(to)?.capabilities_mut().insert(cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn spawn_thread_for_test(
|
||||||
|
&mut self,
|
||||||
|
pid: ProcessId,
|
||||||
|
priority: Priority,
|
||||||
|
time_slice: TimeSlice,
|
||||||
|
) -> ThreadId {
|
||||||
|
use domain::scheduler::Scheduler;
|
||||||
|
let tid = self.threads.spawn(pid, priority, time_slice, None).expect("spawn");
|
||||||
|
let _ = self.processes.get_mut(pid).map(|p| p.add_thread(tid));
|
||||||
|
let core = self.platform.core_id();
|
||||||
|
self.scheduler.add(tid, priority, core);
|
||||||
|
tid
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn register_irq_for_test(&mut self, irq: types::id::IrqId, thread: ThreadId) {
|
||||||
|
let _ = self.irqs.register(irq, thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn block_thread_for_test(
|
||||||
|
&mut self,
|
||||||
|
tid: ThreadId,
|
||||||
|
reason: domain::thread::BlockReason,
|
||||||
|
) {
|
||||||
|
use domain::scheduler::Scheduler;
|
||||||
|
let core = self.platform.core_id();
|
||||||
|
if let Ok(t) = self.threads.get_mut(tid) {
|
||||||
|
if matches!(t.state(), ThreadState::Ready) {
|
||||||
|
let _ = t.set_running(core);
|
||||||
|
}
|
||||||
|
let _ = t.block(reason);
|
||||||
|
}
|
||||||
|
self.scheduler.remove(tid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
mod exit;
|
mod exit;
|
||||||
mod simple;
|
mod simple;
|
||||||
|
mod tick;
|
||||||
|
|
||||||
use domain::memory::page_pool::PagePool;
|
use domain::memory::page_pool::PagePool;
|
||||||
use platform::Platform;
|
use platform::Platform;
|
||||||
|
|||||||
55
crates/kernel/src/tests/tick.rs
Normal file
55
crates/kernel/src/tests/tick.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
use super::*;
|
||||||
|
use domain::scheduler::SchedulerDecision;
|
||||||
|
use types::id::{CoreId, IrqId, ThreadId};
|
||||||
|
use types::time::TimeSlice;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick_continues_when_time_remaining() {
|
||||||
|
let (mut k, _) = kernel_with_process();
|
||||||
|
let decision = k.handle_tick(CoreId::from_raw(0));
|
||||||
|
assert!(matches!(decision, SchedulerDecision::Continue));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tick_preempts_when_time_expired_and_other_thread_ready() {
|
||||||
|
let mut k = test_kernel();
|
||||||
|
let mock = MockPlatform::new(1);
|
||||||
|
let pool = PagePool::new(PhysicalAddress::from_raw(0x8000_0000), 1024);
|
||||||
|
let mut k = Kernel::new(mock, pool);
|
||||||
|
|
||||||
|
// Boot with quantum=2 so it expires after 2 ticks
|
||||||
|
let (pid, tid1) = k.boot(Priority::new(16).unwrap(), TimeSlice::new(2)).unwrap();
|
||||||
|
|
||||||
|
// Spawn a second thread so scheduler has someone to switch to
|
||||||
|
let tid2 = k.spawn_thread_for_test(pid, Priority::new(16).unwrap(), TimeSlice::new(100));
|
||||||
|
|
||||||
|
// First tick: time remaining
|
||||||
|
let d1 = k.handle_tick(CoreId::from_raw(0));
|
||||||
|
assert!(matches!(d1, SchedulerDecision::Continue));
|
||||||
|
|
||||||
|
// Second tick: time expired, should preempt
|
||||||
|
let d2 = k.handle_tick(CoreId::from_raw(0));
|
||||||
|
assert!(matches!(d2, SchedulerDecision::Preempt(_)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn interrupt_unknown_irq_returns_none() {
|
||||||
|
let (mut k, _) = kernel_with_process();
|
||||||
|
assert!(k.handle_interrupt(IrqId::from_raw(99)).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn interrupt_unblocks_registered_thread() {
|
||||||
|
let (mut k, _) = kernel_with_process();
|
||||||
|
let tid = ThreadId::from_raw(0);
|
||||||
|
let irq = IrqId::from_raw(5);
|
||||||
|
|
||||||
|
// Register and block the thread on this IRQ
|
||||||
|
k.register_irq_for_test(irq, tid);
|
||||||
|
k.block_thread_for_test(tid, domain::thread::BlockReason::WaitingForInterrupt(
|
||||||
|
types::bounded::CapSlot::new(0).unwrap()
|
||||||
|
));
|
||||||
|
|
||||||
|
let result = k.handle_interrupt(irq);
|
||||||
|
assert_eq!(result, Some(tid));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user