Refactor: Move interrupt wakers/operations into separate modules

This commit is contained in:
Philipp Oppermann
2020-03-09 08:22:43 +01:00
parent 08582948c5
commit 3d485535ae
9 changed files with 106 additions and 74 deletions

View File

@@ -1,11 +1,38 @@
use crate::{interrupts, print}; use crate::{print, println, task::interrupt_wake};
use conquer_once::spin::OnceCell;
use core::future::Future; use core::future::Future;
use core::{ use core::{
pin::Pin, pin::Pin,
task::{Context, Poll}, task::{Context, Poll},
}; };
use crossbeam_queue::ArrayQueue;
use futures_util::task::AtomicWaker;
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1}; use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
static WAKER: AtomicWaker = AtomicWaker::new();
static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
pub fn init() {
SCANCODE_QUEUE
.try_init_once(|| ArrayQueue::new(10))
.expect("failed to init scancode queue");
}
/// Called by the keyboard interrupt handler
///
/// Must not block (including spinlocks).
pub(crate) fn keyboard_scancode(scancode: u8) {
let scancode_queue = SCANCODE_QUEUE
.try_get()
.expect("scancode queue not initialized");
if let Err(_) = scancode_queue.push(scancode) {
println!("WARNING: dropping keyboard input");
}
if let Some(waker) = WAKER.take() {
interrupt_wake(waker);
}
}
fn next_scancode() -> impl Future<Output = u8> { fn next_scancode() -> impl Future<Output = u8> {
NextScancode NextScancode
} }
@@ -16,7 +43,7 @@ impl Future for NextScancode {
type Output = u8; type Output = u8;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<u8> { fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<u8> {
let scancodes = interrupts::SCANCODE_QUEUE let scancodes = SCANCODE_QUEUE
.try_get() .try_get()
.expect("scancode queue not initialized"); .expect("scancode queue not initialized");
// fast path // fast path
@@ -24,7 +51,7 @@ impl Future for NextScancode {
return Poll::Ready(scancode); return Poll::Ready(scancode);
} }
interrupts::KEYBOARD_INTERRUPT_WAKER.register(&cx.waker()); WAKER.register(&cx.waker());
match scancodes.pop() { match scancodes.pop() {
Ok(scancode) => Poll::Ready(scancode), Ok(scancode) => Poll::Ready(scancode),
Err(crossbeam_queue::PopError) => Poll::Pending, Err(crossbeam_queue::PopError) => Poll::Pending,
@@ -32,7 +59,7 @@ impl Future for NextScancode {
} }
} }
pub async fn print_keypresses() { pub async fn keyboard_task() {
let mut keyboard = Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore); let mut keyboard = Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore);
loop { loop {

View File

@@ -1,2 +1,6 @@
pub mod keyboard; pub mod keyboard;
pub mod timer; pub mod timer;
pub fn init() {
keyboard::init();
}

View File

@@ -1,14 +1,30 @@
use crate::{interrupts, print}; use crate::{print, task::interrupt_wake};
use core::future::Future; use core::future::Future;
use core::{ use core::{
pin::Pin, pin::Pin,
sync::atomic::Ordering, sync::atomic::{AtomicU64, Ordering},
task::{Context, Poll}, task::{Context, Poll},
}; };
use futures_util::task::AtomicWaker;
static TICKS: AtomicU64 = AtomicU64::new(0);
static WAKER: AtomicWaker = AtomicWaker::new();
/// Called by the timer interrupt handler
///
/// Must not block (including spinlocks).
pub(crate) fn tick() {
TICKS.fetch_add(1, Ordering::Release);
if let Some(waker) = WAKER.take() {
interrupt_wake(waker);
}
}
fn next_tick() -> impl Future<Output = u64> {
static NEXT_TICK: AtomicU64 = AtomicU64::new(1);
fn next_tick(current_tick: u64) -> impl Future<Output = u64> {
NextTick { NextTick {
ticks: current_tick, ticks: NEXT_TICK.fetch_add(1, Ordering::Release),
} }
} }
@@ -20,15 +36,8 @@ impl Future for NextTick {
type Output = u64; type Output = u64;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<u64> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<u64> {
let current_ticks = interrupts::TIMER_TICKS.load(Ordering::Acquire); WAKER.register(&cx.waker());
let current_ticks = TICKS.load(Ordering::Acquire);
if self.ticks < current_ticks {
self.ticks += 1;
return Poll::Ready(self.ticks);
}
interrupts::TIMER_INTERRUPT_WAKER.register(&cx.waker());
let current_ticks = interrupts::TIMER_TICKS.load(Ordering::Acquire);
if self.ticks < current_ticks { if self.ticks < current_ticks {
self.ticks += 1; self.ticks += 1;
Poll::Ready(self.ticks) Poll::Ready(self.ticks)
@@ -38,11 +47,9 @@ impl Future for NextTick {
} }
} }
pub async fn print_ticks() { pub async fn timer_task() {
let mut current_ticks = interrupts::TIMER_TICKS.load(Ordering::Acquire);
loop { loop {
current_ticks = next_tick(current_ticks).await; next_tick().await;
print!("."); print!(".");
} }
} }

View File

@@ -1,11 +1,4 @@
use crate::{gdt, hlt_loop, println}; use crate::{gdt, hlt_loop, println};
use conquer_once::spin::OnceCell;
use core::{
sync::atomic::{AtomicU64, Ordering},
task::Waker,
};
use crossbeam_queue::ArrayQueue;
use futures_util::task::AtomicWaker;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use pic8259_simple::ChainedPics; use pic8259_simple::ChainedPics;
use spin; use spin;
@@ -54,23 +47,6 @@ pub fn init_idt() {
IDT.load(); IDT.load();
} }
pub fn init_queues() {
INTERRUPT_WAKEUPS
.try_init_once(|| ArrayQueue::new(10))
.expect("failed to init interrupt wakeup queue");
SCANCODE_QUEUE
.try_init_once(|| ArrayQueue::new(10))
.expect("failed to init scancode queue");
}
static INTERRUPT_WAKEUPS: OnceCell<ArrayQueue<Waker>> = OnceCell::uninit();
pub(crate) fn interrupt_wakeups() -> &'static ArrayQueue<Waker> {
INTERRUPT_WAKEUPS
.try_get()
.expect("interrupt wakeup queue not initialized")
}
extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFrame) { extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFrame) {
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame); println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
} }
@@ -95,41 +71,21 @@ extern "x86-interrupt" fn double_fault_handler(
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame); panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
} }
pub(crate) static TIMER_TICKS: AtomicU64 = AtomicU64::new(0);
pub(crate) static TIMER_INTERRUPT_WAKER: AtomicWaker = AtomicWaker::new();
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: &mut InterruptStackFrame) { extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: &mut InterruptStackFrame) {
TIMER_TICKS.fetch_add(1, Ordering::Release); crate::driver::timer::tick();
if let Some(waker) = TIMER_INTERRUPT_WAKER.take() {
if let Err(_) = interrupt_wakeups().push(waker) {
println!("WARNING: dropping interrupt wakeup");
}
}
unsafe { unsafe {
PICS.lock() PICS.lock()
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8()); .notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
} }
} }
pub(crate) static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
pub(crate) static KEYBOARD_INTERRUPT_WAKER: AtomicWaker = AtomicWaker::new();
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut InterruptStackFrame) { extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut InterruptStackFrame) {
use x86_64::instructions::port::Port; use x86_64::instructions::port::Port;
let mut port = Port::new(0x60); let mut port = Port::new(0x60);
let scancode: u8 = unsafe { port.read() }; let scancode: u8 = unsafe { port.read() };
let scancode_queue = SCANCODE_QUEUE crate::driver::keyboard::keyboard_scancode(scancode);
.try_get()
.expect("scancode queue not initialized");
if let Err(_) = scancode_queue.push(scancode) {
println!("WARNING: dropping keyboard input");
}
if let Some(waker) = KEYBOARD_INTERRUPT_WAKER.take() {
if let Err(_) = interrupt_wakeups().push(waker) {
println!("WARNING: dropping interrupt wakeup");
}
}
unsafe { unsafe {
PICS.lock() PICS.lock()

View File

@@ -31,6 +31,11 @@ pub fn init() {
unsafe { interrupts::PICS.lock().initialize() }; unsafe { interrupts::PICS.lock().initialize() };
} }
pub fn init_heap_structures() {
task::init();
driver::init();
}
pub fn test_runner(tests: &[&dyn Fn()]) { pub fn test_runner(tests: &[&dyn Fn()]) {
serial_println!("Running {} tests", tests.len()); serial_println!("Running {} tests", tests.len());
for test in tests { for test in tests {

View File

@@ -26,7 +26,7 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_map) }; let mut frame_allocator = unsafe { BootInfoFrameAllocator::init(&boot_info.memory_map) };
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed"); allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
blog_os::interrupts::init_queues(); blog_os::init_heap_structures();
// allocate a number on the heap // allocate a number on the heap
let heap_value = Box::new(41); let heap_value = Box::new(41);
@@ -66,8 +66,8 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
println!("It did not crash!"); println!("It did not crash!");
}); });
executor.spawn(blog_os::driver::timer::print_ticks()); executor.spawn(blog_os::driver::timer::timer_task());
executor.spawn(blog_os::driver::keyboard::print_keypresses()); executor.spawn(blog_os::driver::keyboard::keyboard_task());
executor.run(); executor.run();
} }

View File

@@ -1,4 +1,5 @@
use crate::{interrupts, println}; use super::interrupt_wakeups::interrupt_wakeups;
use crate::println;
use alloc::{ use alloc::{
boxed::Box, boxed::Box,
collections::{BTreeMap, VecDeque}, collections::{BTreeMap, VecDeque},
@@ -66,7 +67,7 @@ impl Executor {
/// might execute arbitrary code, e.g. allocate, which should not be done /// might execute arbitrary code, e.g. allocate, which should not be done
/// in interrupt handlers to avoid deadlocks. /// in interrupt handlers to avoid deadlocks.
fn apply_interrupt_wakeups(&mut self) { fn apply_interrupt_wakeups(&mut self) {
while let Ok(waker) = interrupts::interrupt_wakeups().pop() { while let Ok(waker) = interrupt_wakeups().pop() {
waker.wake(); waker.wake();
} }
} }
@@ -87,7 +88,7 @@ impl Executor {
// disable interrupts to avoid races // disable interrupts to avoid races
x86_64::instructions::interrupts::disable(); x86_64::instructions::interrupts::disable();
// check if relevant interrupts occured since the last check // check if relevant interrupts occured since the last check
if interrupts::interrupt_wakeups().is_empty() { if interrupt_wakeups().is_empty() {
// no interrupts occured -> hlt to wait for next interrupt // no interrupts occured -> hlt to wait for next interrupt
x86_64::instructions::interrupts::enable_interrupts_and_hlt(); x86_64::instructions::interrupts::enable_interrupts_and_hlt();
} else { } else {

View File

@@ -0,0 +1,25 @@
use crate::println;
use conquer_once::spin::OnceCell;
use core::task::Waker;
use crossbeam_queue::ArrayQueue;
static INTERRUPT_WAKEUPS: OnceCell<ArrayQueue<Waker>> = OnceCell::uninit();
pub fn init() {
INTERRUPT_WAKEUPS
.try_init_once(|| ArrayQueue::new(10))
.expect("failed to init interrupt wakeup queue");
}
/// Queues a waker for waking in an interrupt-safe way
pub(crate) fn interrupt_wake(waker: Waker) {
if let Err(_) = interrupt_wakeups().push(waker) {
println!("WARNING: dropping interrupt wakeup");
}
}
pub(super) fn interrupt_wakeups() -> &'static ArrayQueue<Waker> {
INTERRUPT_WAKEUPS
.try_get()
.expect("interrupt wakeup queue not initialized")
}

View File

@@ -1 +1,8 @@
pub(crate) use interrupt_wakeups::interrupt_wake;
pub mod executor; pub mod executor;
mod interrupt_wakeups;
pub fn init() {
interrupt_wakeups::init();
}