From 558248a070c64903ca1387d76b1c716e4195cd07 Mon Sep 17 00:00:00 2001 From: Chen Chengjun Date: Fri, 31 May 2024 12:24:46 +0800 Subject: [PATCH] Enable softirq mechanism --- Cargo.lock | 7 + framework/aster-frame/Cargo.toml | 3 +- framework/aster-frame/src/trap/handler.rs | 5 + framework/aster-frame/src/trap/mod.rs | 3 + framework/aster-frame/src/trap/softirq.rs | 163 ++++++++++++++++++ kernel/aster-nix/src/softirq_id.rs | 13 ++ .../aster-nix/src/time/clocks/system_wide.rs | 17 +- kernel/aster-nix/src/time/mod.rs | 2 + kernel/aster-nix/src/time/softirq.rs | 34 ++++ 9 files changed, 236 insertions(+), 11 deletions(-) create mode 100644 framework/aster-frame/src/trap/softirq.rs create mode 100644 kernel/aster-nix/src/softirq_id.rs create mode 100644 kernel/aster-nix/src/time/softirq.rs diff --git a/Cargo.lock b/Cargo.lock index a53f9f8e2..fe33243b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,6 +54,12 @@ dependencies = [ "spinning_top", ] +[[package]] +name = "array-init" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" + [[package]] name = "arrayvec" version = "0.5.2" @@ -102,6 +108,7 @@ dependencies = [ "acpi", "align_ext", "aml", + "array-init", "aster-main", "bit_field", "bitflags 1.3.2", diff --git a/framework/aster-frame/Cargo.toml b/framework/aster-frame/Cargo.toml index 924f62ce3..abba710ae 100644 --- a/framework/aster-frame/Cargo.toml +++ b/framework/aster-frame/Cargo.toml @@ -20,7 +20,8 @@ xarray = { git = "https://github.com/asterinas/xarray", rev = "72a4067" } int-to-c-enum = { path = "../../kernel/libs/int-to-c-enum" } # instrusive-collections of version 0.9.6 fails to compile with current rust toolchain, # So we set a fixed version 0.9.5 for this crate -intrusive-collections = "=0.9.5" +intrusive-collections = { version = "=0.9.5", features = ["nightly"] } +array-init = "2.0" ktest = { path = "../libs/ktest" } id-alloc = { path = "../libs/id-alloc" } lazy_static = { version = "1.0", features = ["spin_no_std"] } diff --git a/framework/aster-frame/src/trap/handler.rs b/framework/aster-frame/src/trap/handler.rs index 79eb01a80..5ff07577e 100644 --- a/framework/aster-frame/src/trap/handler.rs +++ b/framework/aster-frame/src/trap/handler.rs @@ -18,11 +18,16 @@ pub(crate) fn call_irq_callback_functions(trap_frame: &TrapFrame) { for callback_function in callback_functions.iter() { callback_function.call(trap_frame); } + drop(callback_functions); + if !CpuException::is_cpu_exception(trap_frame.trap_num as u16) { crate::arch::interrupts_ack(); } IN_INTERRUPT_CONTEXT.store(false, Ordering::Release); + + crate::arch::irq::enable_local(); + crate::trap::softirq::process_pending(); } cpu_local! { diff --git a/framework/aster-frame/src/trap/mod.rs b/framework/aster-frame/src/trap/mod.rs index 054f7c574..f98c2982e 100644 --- a/framework/aster-frame/src/trap/mod.rs +++ b/framework/aster-frame/src/trap/mod.rs @@ -2,8 +2,10 @@ mod handler; mod irq; +pub mod softirq; pub use handler::in_interrupt_context; +pub use softirq::SoftIrqLine; pub use trapframe::TrapFrame; pub(crate) use self::handler::call_irq_callback_functions; @@ -15,4 +17,5 @@ pub(crate) fn init() { unsafe { trapframe::init(); } + softirq::init(); } diff --git a/framework/aster-frame/src/trap/softirq.rs b/framework/aster-frame/src/trap/softirq.rs new file mode 100644 index 000000000..049151785 --- /dev/null +++ b/framework/aster-frame/src/trap/softirq.rs @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::boxed::Box; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; + +use spin::Once; + +use crate::{cpu_local, task::disable_preempt, CpuLocal}; + +/// A representation of a software interrupt (softirq) line. +/// +/// # Overview +/// +/// Softirq is an interrupt mechanism in the kernel that enables bottom-half processing; +/// they are cheaper to execute compared to regular interrupts because softirqs are less +/// time-critical and thus can be processed in a more flexible manner. +/// +/// The `SoftIrqLine` struct encapsulates the data and functionality associated with each +/// softirq line, including an identifier and an associated callback that gets triggered +/// when the softirq is raised. +/// +/// The `SoftIrqLine` with the smaller ID has the higher execution priority. +/// +/// # Example +/// +/// ``` +/// // Define an unused softirq id. +/// const MY_SOFTIRQ_ID: u8 = 4; +/// // Enable the softirq line of this id. +/// SoftIrqLine::get(MY_SOFTIRQ_ID).enable(|| { +/// // Define the action to take when the softirq with MY_SOFTIRQ_ID is raised +/// ... +/// }); +/// // Later on: +/// SoftIrqLine::get(MY_SOFTIRQ_ID).raise(); // This will trigger the registered callback +/// ``` +pub struct SoftIrqLine { + id: u8, + callback: Once>, +} + +impl SoftIrqLine { + /// The number of softirq lines. + const NR_LINES: u8 = 8; + + /// Gets a softirq line. + /// + /// The value of `id` must be within `0..NR_LINES`. + pub fn get(id: u8) -> &'static SoftIrqLine { + &LINES.get().unwrap()[id as usize] + } + + const fn new(id: u8) -> Self { + Self { + id, + callback: Once::new(), + } + } + + /// Gets the ID of this softirq line. + pub fn id(&self) -> u8 { + self.id + } + + /// Raises the softirq, marking it as pending. + /// + /// If this line is not enabled yet, the method has no effect. + pub fn raise(&self) { + CpuLocal::borrow_with(&PENDING_MASK, |mask| { + mask.fetch_or(1 << self.id, Ordering::Release); + }); + } + + /// Enables a softirq line by registering its callback. + /// + /// # Panic + /// + /// Each softirq can only be enabled once. + pub fn enable(&self, callback: F) + where + F: Fn() + 'static + Sync + Send, + { + assert!(!self.is_enabled()); + + self.callback.call_once(|| Box::new(callback)); + ENABLED_MASK.fetch_or(1 << self.id, Ordering::Release); + } + + /// Returns whether this softirq line is enabled. + pub fn is_enabled(&self) -> bool { + ENABLED_MASK.load(Ordering::Acquire) & (1 << self.id) != 0 + } +} + +/// A slice that stores the `SoftIrqLine`s, whose ID is equal to its offset in the slice. +static LINES: Once<[SoftIrqLine; SoftIrqLine::NR_LINES as usize]> = Once::new(); + +pub(super) fn init() { + let lines: [SoftIrqLine; SoftIrqLine::NR_LINES as usize] = + array_init::array_init(|i| SoftIrqLine::new(i as u8)); + LINES.call_once(|| lines); +} + +static ENABLED_MASK: AtomicU8 = AtomicU8::new(0); + +cpu_local! { + static PENDING_MASK: AtomicU8 = AtomicU8::new(0); + static IS_ENABLED: AtomicBool = AtomicBool::new(true); +} + +/// Enables softirq in current processor. +fn enable_softirq_local() { + CpuLocal::borrow_with(&IS_ENABLED, |is_enabled| { + is_enabled.store(true, Ordering::Release) + }) +} + +/// Disables softirq in current processor. +fn disable_softirq_local() { + CpuLocal::borrow_with(&IS_ENABLED, |is_enabled| { + is_enabled.store(false, Ordering::Release) + }) +} + +/// Checks whether the softirq is enabled in current processor. +fn is_softirq_enabled() -> bool { + CpuLocal::borrow_with(&IS_ENABLED, |is_enabled| is_enabled.load(Ordering::Acquire)) +} + +/// Processes pending softirqs. +/// +/// The processing instructions will iterate for `SOFTIRQ_RUN_TIMES` times. If any softirq +/// is raised during the iteration, it will be processed. +pub(crate) fn process_pending() { + const SOFTIRQ_RUN_TIMES: u8 = 5; + + if !is_softirq_enabled() { + return; + } + + let preempt_guard = disable_preempt(); + disable_softirq_local(); + + CpuLocal::borrow_with(&PENDING_MASK, |mask| { + for i in 0..SOFTIRQ_RUN_TIMES { + // will not reactive in this handling. + let mut action_mask = { + let pending_mask = mask.fetch_and(0, Ordering::Acquire); + pending_mask & ENABLED_MASK.load(Ordering::Acquire) + }; + + if action_mask == 0 { + return; + } + while action_mask > 0 { + let action_id = u8::trailing_zeros(action_mask) as u8; + SoftIrqLine::get(action_id).callback.get().unwrap()(); + action_mask &= action_mask - 1; + } + } + }); + enable_softirq_local(); +} diff --git a/kernel/aster-nix/src/softirq_id.rs b/kernel/aster-nix/src/softirq_id.rs new file mode 100644 index 000000000..db0f8a35f --- /dev/null +++ b/kernel/aster-nix/src/softirq_id.rs @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! Defines the used IDs of software interrupt (softirq) lines. + +/// The corresponding softirq line is used to schedule urgent taskless jobs. +pub const TASKLESS_URGENT_SOFTIRQ_ID: u8 = 0; + +/// The corresponding softirq line is used to manage timers and handle +/// time-related jobs. +pub const TIMER_SOFTIRQ_ID: u8 = 1; + +/// The corresponding softirq line is used to schedule general taskless jobs. +pub const TASKLESS_SOFTIRQ_ID: u8 = 2; diff --git a/kernel/aster-nix/src/time/clocks/system_wide.rs b/kernel/aster-nix/src/time/clocks/system_wide.rs index 4224b4cc6..defc49473 100644 --- a/kernel/aster-nix/src/time/clocks/system_wide.rs +++ b/kernel/aster-nix/src/time/clocks/system_wide.rs @@ -3,17 +3,14 @@ use alloc::sync::Arc; use core::time::Duration; -use aster_frame::{ - arch::timer::{self, Jiffies}, - cpu_local, - sync::SpinLock, - CpuLocal, -}; +use aster_frame::{arch::timer::Jiffies, cpu_local, sync::SpinLock, CpuLocal}; use aster_time::read_monotonic_time; use paste::paste; use spin::Once; -use crate::time::{system_time::START_TIME_AS_DURATION, timer::TimerManager, Clock, SystemTime}; +use crate::time::{ + self, system_time::START_TIME_AS_DURATION, timer::TimerManager, Clock, SystemTime, +}; /// The Clock that reads the jiffies, and turn the counter into `Duration`. pub struct JiffiesClock { @@ -220,7 +217,7 @@ macro_rules! define_timer_managers { let callback = move || { clock_manager.process_expired_timers(); }; - timer::register_callback(callback); + time::softirq::register_callback(callback); )* } } @@ -258,7 +255,7 @@ fn init_jiffies_clock_manager() { let callback = move || { jiffies_timer_manager.process_expired_timers(); }; - timer::register_callback(callback); + time::softirq::register_callback(callback); } fn update_coarse_clock() { @@ -270,7 +267,7 @@ fn update_coarse_clock() { fn init_coarse_clock() { let real_time = RealTimeClock::get().read_time(); RealTimeCoarseClock::current_ref().call_once(|| SpinLock::new(real_time)); - timer::register_callback(update_coarse_clock); + time::softirq::register_callback(update_coarse_clock); } pub(super) fn init() { diff --git a/kernel/aster-nix/src/time/mod.rs b/kernel/aster-nix/src/time/mod.rs index c800b19cb..f10849787 100644 --- a/kernel/aster-nix/src/time/mod.rs +++ b/kernel/aster-nix/src/time/mod.rs @@ -12,6 +12,7 @@ use crate::prelude::*; pub mod clocks; mod core; +mod softirq; mod system_time; pub mod wait; @@ -23,6 +24,7 @@ pub type clock_t = i64; pub(super) fn init() { system_time::init(); clocks::init(); + softirq::init(); } #[repr(C)] diff --git a/kernel/aster-nix/src/time/softirq.rs b/kernel/aster-nix/src/time/softirq.rs new file mode 100644 index 000000000..cee8dc19e --- /dev/null +++ b/kernel/aster-nix/src/time/softirq.rs @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::{boxed::Box, vec::Vec}; + +use aster_frame::{arch::timer, sync::RwLock, trap::SoftIrqLine}; + +use crate::softirq_id::TIMER_SOFTIRQ_ID; + +static TIMER_SOFTIRQ_CALLBACKS: RwLock>> = RwLock::new(Vec::new()); + +pub(super) fn init() { + SoftIrqLine::get(TIMER_SOFTIRQ_ID).enable(timer_softirq_handler); + + timer::register_callback(|| { + SoftIrqLine::get(TIMER_SOFTIRQ_ID).raise(); + }); +} + +/// Registers a function that will be executed during timer softirq. +pub(super) fn register_callback(func: F) +where + F: Fn() + Sync + Send + 'static, +{ + TIMER_SOFTIRQ_CALLBACKS + .write_irq_disabled() + .push(Box::new(func)); +} + +fn timer_softirq_handler() { + let callbacks = TIMER_SOFTIRQ_CALLBACKS.read_irq_disabled(); + for callback in callbacks.iter() { + (callback)(); + } +}