So I have two news for you. One good and one bad.

We have async, but we got rid of the preëmptive sched... wait, that's two good news, anyways, have a nice day.

— Erin
This commit is contained in:
Erin 2022-08-07 23:42:23 +02:00 committed by ondra05
parent 8e09af7f15
commit cd8e6e4b3b
11 changed files with 146 additions and 188 deletions

30
Cargo.lock generated
View file

@ -203,6 +203,25 @@ name = "cpuio"
version = "0.3.2" version = "0.3.2"
source = "git+https://git.ablecorp.us/ondra05/cpuio.git#093cc103101b4ba4abd02d77c884113a376cdc64" source = "git+https://git.ablecorp.us/ondra05/cpuio.git#093cc103101b4ba4abd02d77c884113a376cdc64"
[[package]]
name = "crossbeam-queue"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "downcast-rs" name = "downcast-rs"
version = "1.2.0" version = "1.2.0"
@ -295,8 +314,10 @@ dependencies = [
name = "kernel" name = "kernel"
version = "0.1.2" version = "0.1.2"
dependencies = [ dependencies = [
"crossbeam-queue",
"linked_list_allocator", "linked_list_allocator",
"log", "log",
"slab",
"versioning", "versioning",
] ]
@ -647,6 +668,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "slab"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "spin" name = "spin"
version = "0.5.2" version = "0.5.2"

View file

@ -98,34 +98,11 @@ extern "x86-interrupt" fn double_fault_handler(
* As long as nothing in this function does something that would * As long as nothing in this function does something that would
* normally trigger an error, this function is relatively safe. * normally trigger an error, this function is relatively safe.
*/ */
#[naked]
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) { extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
use super::task_switcher; crate::kmain::tick();
unsafe { unsafe {
asm!( PICS.lock()
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
// "call {disable}",
// Kernel tick
"call {tick}",
// Push task's state onto stack
// and save task pointer into scheduler
task_switcher::save_tasks_state!(),
"mov rdi, rsp",
"call {save}",
// "call {enable}",
// Switch to next task (interrupt'll be returned there)
"jmp {switch_to_next}",
tick = sym crate::kmain::tick,
save = sym task_switcher::save_and_enqueue,
// disable = sym x86_64::instructions::interrupts::disable,
// enable = sym x86_64::instructions::interrupts::enable,
switch_to_next = sym task_switcher::switch_to_next,
options(noreturn),
);
} }
} }
@ -192,7 +169,7 @@ pub fn init_idt() {
} }
/// https://wiki.osdev.org/Pit /// https://wiki.osdev.org/Pit
/// ///
const PIT_MAX_FREQ: u32 = 1193182; const PIT_MAX_FREQ: u32 = 1193182;
pub fn set_pit_frequency(pit: u16, freq: u32) { pub fn set_pit_frequency(pit: u16, freq: u32) {

View file

@ -10,8 +10,6 @@ pub mod init;
pub mod interrupts; pub mod interrupts;
pub mod memory; pub mod memory;
mod task_switcher;
use crate::arch::drivers::allocator; use crate::arch::drivers::allocator;
use bootloader::{entry_point, BootInfo}; use bootloader::{entry_point, BootInfo};
use x86_64::{instructions::hlt, VirtAddr}; use x86_64::{instructions::hlt, VirtAddr};

View file

@ -1,99 +0,0 @@
use crate::scheduler::{Task, SCHEDULER};
/// Saves task's state onto stack
macro_rules! save_tasks_state {
() => {
"
// Copy task's state
push [rsp + 32] // SS
push [rsp + 32] // RSP
push [rsp + 32] // RFLAGS
push [rsp + 32] // CS
push [rsp + 32] // RIP
// Save task's registers
push r15
push r14
push r13
push r12
push r11
push r10
push r9
push r8
push rdi
push rsi
push rdx
push rcx
push rbx
push rax
"
};
}
pub(super) use save_tasks_state;
/// Save provided stack pointer into scheduler's queue
pub extern "C" fn save_and_enqueue(sp: u64) {
SCHEDULER.lock().enqueue_suspended(sp);
}
// Fetch and load next task
pub unsafe extern "C" fn switch_to_next() -> ! {
// Fetch next task
let next = SCHEDULER.lock().pop().expect("no task in the task queue");
match next {
Task::Suspended(sp) => asm!(
// Restore task's registers
"mov rsp, {}",
"pop rax",
"pop rbx",
"pop rcx",
"pop rdx",
"pop rsi",
"pop rdi",
"pop r8",
"pop r9",
"pop r10",
"pop r11",
"pop r12",
"pop r13",
"pop r14",
"pop r15",
// Copy things up the stack
"add rsp, 80", // Move 8 bytes above exception stack frame
"push [rsp - 48]", // SS
"push [rsp - 48]", // RSP
"push [rsp - 48]", // RFLAGS
"push [rsp - 48]", // CS
"push [rsp - 48]", // RIP
// Signalise end of the interrupt and return
"push rax",
"mov al, 32",
"out 20h, al",
"pop rax",
"iretq",
in(reg) sp,
options(noreturn),
),
Task::Spawn {
function,
stack_start,
} => asm!(
"add rsp, 160", // Move above saved registries
"mov [rsp], {ip}", // Set task's instruction pointer
"mov [rsp + 24], {sp}", // Set task's stack pointer
// Signalise the end of the interrupt and return
"mov al, 32",
"out 20h, al",
"iretq",
ip = in(reg) function,
sp = in(reg) stack_start,
options(noreturn),
),
}
}

View file

@ -6,7 +6,7 @@ use crate::arch::{drivers::sysinfo::master, init, sloop};
use crate::hardware; use crate::hardware;
use crate::relib::network::socket::{SimpleSock, Socket}; use crate::relib::network::socket::{SimpleSock, Socket};
use crate::{ use crate::{
boot_conf::KernelConfig, scheduler::SCHEDULER, scratchpad, systeminfo::RELEASE_TYPE, TERM, boot_conf::KernelConfig, scratchpad, systeminfo::RELEASE_TYPE, TERM,
}; };
use kernel::KERNEL_VERSION; use kernel::KERNEL_VERSION;
use spin::Lazy; use spin::Lazy;
@ -66,16 +66,7 @@ pub fn kernel_main() -> ! {
/* /*
log_version_data(); log_version_data();
// */ // */
x86_64::instructions::interrupts::without_interrupts(|| { scratchpad();
let mut scheduler = SCHEDULER.lock();
// comment this out to resume normal use
// scheduler.enqueue_spawn(traceloop);
scheduler.enqueue_spawn(scratchpad);
});
// scratchpad();
sloop() sloop()
} }

View file

@ -66,7 +66,6 @@ pub mod prelude;
pub mod ps2_mouse; pub mod ps2_mouse;
pub mod relib; pub mod relib;
pub mod rhai_shell; pub mod rhai_shell;
pub mod scheduler;
pub mod scratchpad; pub mod scratchpad;
pub mod stdio; pub mod stdio;
pub mod time; pub mod time;

View file

@ -1,46 +0,0 @@
use alloc::collections::VecDeque;
use spin::{Lazy, Mutex};
pub static SCHEDULER: Lazy<Mutex<Scheduler>> = Lazy::new(|| Mutex::new(Scheduler::default()));
/// Task's stack size (in kiB)
const STACK_SIZE: usize = 512;
pub enum Task {
Suspended(u64),
Spawn { function: fn(), stack_start: usize },
}
#[derive(Default)]
pub struct Scheduler {
task_queue: VecDeque<Task>,
}
impl Scheduler {
pub fn enqueue_spawn(&mut self, function: fn()) {
self.task_queue.push_back(Task::Spawn {
function,
stack_start: unsafe {
let size = STACK_SIZE * 1024;
alloc::alloc::alloc(
alloc::alloc::Layout::from_size_align(size, 1).expect("invalid layout"),
)
.add(size - 1) as _
},
});
}
pub fn enqueue_suspended(&mut self, sp: u64) {
self.task_queue.push_back(Task::Suspended(sp));
}
pub fn pop(&mut self) -> Option<Task> {
self.task_queue.pop_front()
}
}
pub enum WakeConditions {
Time,
ReceivedMessageInChannel,
GainedAccessToChannel,
}

View file

@ -6,6 +6,12 @@ version = "0.1.2"
[dependencies] [dependencies]
linked_list_allocator = "0.9" linked_list_allocator = "0.9"
log = "0.4.14" log = "0.4.14"
slab = { version = "0.4", default-features = false }
[dependencies.crossbeam-queue]
version = "0.3"
default-features = false
features = ["alloc"]
[dependencies.versioning] [dependencies.versioning]
git = "https://git.ablecorp.us/able/aos_userland" git = "https://git.ablecorp.us/able/aos_userland"

View file

@ -16,6 +16,7 @@ pub mod messaging;
// pub mod panic; // pub mod panic;
pub mod proccess; pub mod proccess;
pub mod syscalls; pub mod syscalls;
pub mod task;
pub mod time; pub mod time;
use core::arch::asm; use core::arch::asm;

101
kernel/src/task.rs Normal file
View file

@ -0,0 +1,101 @@
//! Async task and executor
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, task::Wake};
use core::{
future::Future,
pin::Pin,
task::{Context, Poll, Waker},
};
use crossbeam_queue::SegQueue;
use slab::Slab;
type TaskQueue = Arc<SegQueue<TaskId>>;
/// Tasks executor
#[derive(Default)]
pub struct Executor {
/// All spawned tasks' stash
tasks: Slab<Task>,
/// Awake tasks' queue
queue: TaskQueue,
/// Wakers
wakers: BTreeMap<TaskId, Waker>,
}
impl Executor {
/// Spawn a future
pub fn spawn(&mut self, future: impl Future<Output = ()> + 'static) {
self.queue
.push(TaskId(self.tasks.insert(Task::new(future))));
}
/// Run tasks
pub fn run(&mut self) -> ! {
loop {
while let Some(id) = self.queue.pop() {
let task = match self.tasks.get_mut(id.0) {
Some(t) => t,
None => continue,
};
let mut cx = Context::from_waker(
self.wakers
.entry(id)
.or_insert_with(|| TaskWaker::new(id, Arc::clone(&self.queue))),
);
match task.poll(&mut cx) {
Poll::Ready(()) => {
self.tasks.remove(id.0);
self.wakers.remove(&id);
}
Poll::Pending => (),
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct TaskId(usize);
/// Async task
struct Task {
future: Pin<Box<dyn Future<Output = ()>>>,
}
impl Task {
/// Create a new task from a future
fn new(future: impl Future<Output = ()> + 'static) -> Self {
Self {
future: Box::pin(future),
}
}
fn poll(&mut self, cx: &mut Context) -> Poll<()> {
self.future.as_mut().poll(cx)
}
}
struct TaskWaker {
id: TaskId,
queue: TaskQueue,
}
impl TaskWaker {
fn new(id: TaskId, queue: TaskQueue) -> Waker {
Waker::from(Arc::new(Self { id, queue }))
}
}
impl Wake for TaskWaker {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
self.queue.push(self.id);
}
}

View file

@ -29,8 +29,8 @@ impl Subcommand {
match str.as_ref() { match str.as_ref() {
"doc" => Subcommand::Doc, "doc" => Subcommand::Doc,
"help" => Subcommand::Help, "help" => Subcommand::Help,
"run" => Subcommand::Run, "run" | "r" => Subcommand::Run,
"test" => Subcommand::Test, "test" | "t" => Subcommand::Test,
"" => Subcommand::Empty, "" => Subcommand::Empty,
unknown => Subcommand::Unknown(unknown.to_string()), unknown => Subcommand::Unknown(unknown.to_string()),
} }