diff --git a/Cargo.toml b/Cargo.toml index ffbc53bc8d..1780c94510 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ vga = [] virtio = ["dep:virtio"] virtio-net = ["net", "virtio"] vsock = ["virtio", "pci"] +preemptive-multithreading = [] [lints.rust] rust_2018_idioms = "warn" diff --git a/src/arch/x86_64/kernel/apic.rs b/src/arch/x86_64/kernel/apic.rs index 865e44faed..794b22a0fd 100644 --- a/src/arch/x86_64/kernel/apic.rs +++ b/src/arch/x86_64/kernel/apic.rs @@ -21,6 +21,8 @@ use x86_64::registers::control::Cr3; use x86_64::registers::model_specific::Msr; use super::interrupts::IDT; +#[cfg(feature = "preemptive-multithreading")] +use crate::arch::scheduler::MIN_RESCHEDULE_INTERVAL_US; use crate::arch::x86_64::kernel::CURRENT_STACK_ADDRESS; #[cfg(feature = "acpi")] use crate::arch::x86_64::kernel::acpi; @@ -683,8 +685,26 @@ fn calibrate_timer() { ); } -fn __set_oneshot_timer(wakeup_time: Option) { - if let Some(wt) = wakeup_time { +fn __set_oneshot_timer_relative(ticks: u64) { + #[cfg(feature = "preemptive-multithreading")] + let ticks = cmp::min(ticks, MIN_RESCHEDULE_INTERVAL_US); + + let ticks = cmp::min( + CALIBRATED_COUNTER_VALUE.get().unwrap() * ticks, + u64::from(u32::MAX), + ); + + __set_oneshot_timer_ticks(ticks); +} + +fn __set_oneshot_timer_ticks(ticks: u64) { + // Enable the APIC Timer in One-Shot Mode and let it start by setting the initial counter value. + local_apic_write(IA32_X2APIC_LVT_TIMER, u64::from(TIMER_INTERRUPT_NUMBER)); + local_apic_write(IA32_X2APIC_INIT_COUNT, ticks); +} + +fn __set_oneshot_timer(wakeup_time_us: Option) { + if let Some(wt) = wakeup_time_us { if processor::supports_tsc_deadline() { // wt is the absolute wakeup time in microseconds based on processor::get_timer_ticks. // We can simply multiply it by the processor frequency to get the absolute Time-Stamp Counter deadline @@ -710,24 +730,28 @@ fn __set_oneshot_timer(wakeup_time: Option) { } else { 1 }; - let init_count = cmp::min( - CALIBRATED_COUNTER_VALUE.get().unwrap() * ticks, - u64::from(u32::MAX), - ); - // Enable the APIC Timer in One-Shot Mode and let it start by setting the initial counter value. - local_apic_write(IA32_X2APIC_LVT_TIMER, u64::from(TIMER_INTERRUPT_NUMBER)); - local_apic_write(IA32_X2APIC_INIT_COUNT, init_count); + __set_oneshot_timer_relative(ticks); } } else { // Disable the APIC Timer. + #[cfg(not(feature = "preemptive-multithreading"))] local_apic_write(IA32_X2APIC_LVT_TIMER, APIC_LVT_MASK); } } -pub fn set_oneshot_timer(wakeup_time: Option) { +/// Sets a timer at precisely abs_wakeup_time_us after boot +pub fn set_oneshot_timer(abs_wakeup_time_us: Option) { + without_interrupts(|| { + __set_oneshot_timer(abs_wakeup_time_us); + }); +} + +/// Sets a timer in precisely rel_wakeup_time_us microseconds$ +#[cfg(feature = "preemptive-multithreading")] +pub fn set_oneshot_timer_relative(rel_wakeup_time_us: u64) { without_interrupts(|| { - __set_oneshot_timer(wakeup_time); + __set_oneshot_timer_relative(rel_wakeup_time_us); }); } diff --git a/src/arch/x86_64/kernel/scheduler.rs b/src/arch/x86_64/kernel/scheduler.rs index 06469082d9..a2ab941f83 100644 --- a/src/arch/x86_64/kernel/scheduler.rs +++ b/src/arch/x86_64/kernel/scheduler.rs @@ -14,6 +14,8 @@ use free_list::{PageLayout, PageRange}; use memory_addresses::{PhysAddr, VirtAddr}; use super::interrupts::{IDT, IST_SIZE}; +#[cfg(feature = "preemptive-multithreading")] +use crate::arch::kernel::apic::set_oneshot_timer_relative; use crate::arch::x86_64::kernel::core_local::*; use crate::arch::x86_64::kernel::{apic, interrupts}; use crate::arch::x86_64::mm::paging::{ @@ -26,6 +28,10 @@ use crate::mm::virtualmem::KERNEL_FREE_LIST; use crate::scheduler::PerCoreSchedulerExt; use crate::scheduler::task::{Task, TaskFrame}; +// Run scheduler at least every xxx micro-seconds +#[cfg(feature = "preemptive-multithreading")] +pub const MIN_RESCHEDULE_INTERVAL_US: u64 = 50; + #[repr(C, packed)] struct State { #[cfg(feature = "common-os")] @@ -398,6 +404,8 @@ extern "x86-interrupt" fn timer_handler(_stack_frame: interrupts::ExceptionStack increment_irq_counter(apic::TIMER_INTERRUPT_NUMBER); core_scheduler().handle_waiting_tasks(); apic::eoi(); + #[cfg(feature = "preemptive-multithreading")] + set_oneshot_timer_relative(MIN_RESCHEDULE_INTERVAL_US); core_scheduler().reschedule(); } @@ -409,4 +417,7 @@ pub fn install_timer_handler() { .set_stack_index(0); } interrupts::add_irq_name(apic::TIMER_INTERRUPT_NUMBER - 32, "Timer"); + + #[cfg(feature = "preemptive-multithreading")] + set_oneshot_timer_relative(1); }