Unify CPU arch-specific logic that determines if the kernel is interrupted
This commit is contained in:
parent
bd0ab0b8aa
commit
4b87dab86e
|
@ -7,7 +7,7 @@ use alloc::{
|
|||
};
|
||||
|
||||
use id_alloc::IdAlloc;
|
||||
use ostd::{arch::trap::is_kernel_interrupted, sync::Mutex, timer};
|
||||
use ostd::{cpu::PrivilegeLevel, irq::InterruptLevel, sync::Mutex, timer};
|
||||
|
||||
use super::Process;
|
||||
use crate::{
|
||||
|
@ -31,6 +31,19 @@ use crate::{
|
|||
/// invoke the callbacks of expired timers which are based on the updated
|
||||
/// CPU clock.
|
||||
fn update_cpu_time() {
|
||||
// Retrieve some info about the timer interrupt
|
||||
let is_kernel_interrupted = {
|
||||
let interrupt_level = InterruptLevel::current();
|
||||
let InterruptLevel::L1(cpu_priv_at_irq) = interrupt_level else {
|
||||
// We are at the interrupt level 2.
|
||||
// This means that bottom half of IRQ handling is interrupted.
|
||||
// We should not count this time slice on the head of the current task.
|
||||
return;
|
||||
};
|
||||
cpu_priv_at_irq == PrivilegeLevel::Kernel
|
||||
};
|
||||
|
||||
// Retrieve some info about the current task
|
||||
let Some(current_thread) = Thread::current() else {
|
||||
return;
|
||||
};
|
||||
|
@ -42,11 +55,12 @@ fn update_cpu_time() {
|
|||
// `None` here.
|
||||
return;
|
||||
};
|
||||
|
||||
let timer_manager = process.timer_manager();
|
||||
// Based on whether the timer interrupt occurs in kernel mode or user mode,
|
||||
// the function will add the duration of one timer interrupt interval to the
|
||||
// corresponding CPU clocks.
|
||||
if is_kernel_interrupted() {
|
||||
if is_kernel_interrupted {
|
||||
posix_thread.prof_clock().kernel_clock().add_jiffies(1);
|
||||
process.prof_clock().kernel_clock().add_jiffies(1);
|
||||
} else {
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::{
|
|||
mm::tlb_flush_addr,
|
||||
trap::{RawUserContext, TrapFrame},
|
||||
},
|
||||
cpu::PrivilegeLevel,
|
||||
irq::call_irq_callback_functions,
|
||||
user::{ReturnReason, UserContextApi, UserContextApiInternal},
|
||||
};
|
||||
|
@ -219,7 +220,7 @@ impl UserContextApiInternal for UserContext {
|
|||
log::debug!("Handling hardware interrupt: {:?}", interrupt);
|
||||
while let Some(irq) = crate::arch::kernel::irq::claim() {
|
||||
// Call the IRQ callback functions for the claimed interrupt
|
||||
call_irq_callback_functions(&self.as_trap_frame(), irq as _);
|
||||
call_irq_callback_functions(&self.as_trap_frame(), irq as _, PrivilegeLevel::User);
|
||||
}
|
||||
}
|
||||
Interrupt::PMI => todo!(),
|
||||
|
|
|
@ -12,15 +12,11 @@ pub use trap::TrapFrame;
|
|||
|
||||
use crate::{
|
||||
arch::{cpu::context::CpuExceptionInfo, mm::tlb_flush_addr},
|
||||
cpu_local_cell,
|
||||
cpu::PrivilegeLevel,
|
||||
irq::call_irq_callback_functions,
|
||||
mm::MAX_USERSPACE_VADDR,
|
||||
};
|
||||
|
||||
cpu_local_cell! {
|
||||
static IS_KERNEL_INTERRUPTED: bool = false;
|
||||
}
|
||||
|
||||
/// Initialize trap handling on LoongArch.
|
||||
pub(crate) unsafe fn init() {
|
||||
unsafe {
|
||||
|
@ -28,13 +24,6 @@ pub(crate) unsafe fn init() {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns true if this function is called within the context of an IRQ handler
|
||||
/// and the IRQ occurs while the CPU is executing in the kernel mode.
|
||||
/// Otherwise, it returns false.
|
||||
pub fn is_kernel_interrupted() -> bool {
|
||||
IS_KERNEL_INTERRUPTED.load()
|
||||
}
|
||||
|
||||
/// Handle traps (only from kernel).
|
||||
#[no_mangle]
|
||||
extern "C" fn trap_handler(f: &mut TrapFrame) {
|
||||
|
@ -85,7 +74,6 @@ extern "C" fn trap_handler(f: &mut TrapFrame) {
|
|||
Exception::TLBRFill => unreachable!(),
|
||||
},
|
||||
Trap::Interrupt(interrupt) => {
|
||||
IS_KERNEL_INTERRUPTED.store(true);
|
||||
match interrupt {
|
||||
Interrupt::SWI0 => todo!(),
|
||||
Interrupt::SWI1 => todo!(),
|
||||
|
@ -100,14 +88,13 @@ extern "C" fn trap_handler(f: &mut TrapFrame) {
|
|||
log::debug!("Handling hardware interrupt: {:?}", interrupt);
|
||||
while let Some(irq) = crate::arch::kernel::irq::claim() {
|
||||
// Call the IRQ callback functions for the claimed interrupt
|
||||
call_irq_callback_functions(f, irq as _);
|
||||
call_irq_callback_functions(f, irq as _, PrivilegeLevel::Kernel);
|
||||
}
|
||||
}
|
||||
Interrupt::PMI => todo!(),
|
||||
Interrupt::Timer => todo!(),
|
||||
Interrupt::IPI => todo!(),
|
||||
}
|
||||
IS_KERNEL_INTERRUPTED.store(false);
|
||||
}
|
||||
Trap::MachineError(machine_error) => panic!(
|
||||
"Machine error: {machine_error:?}, badv: {badv:#x?}, badi: {badi:#x?}, era: {era:#x?}"
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::{
|
|||
trap::{RawUserContext, TrapFrame},
|
||||
TIMER_IRQ_NUM,
|
||||
},
|
||||
cpu::PrivilegeLevel,
|
||||
irq::call_irq_callback_functions,
|
||||
user::{ReturnReason, UserContextApi, UserContextApiInternal},
|
||||
};
|
||||
|
@ -149,6 +150,7 @@ impl UserContextApiInternal for UserContext {
|
|||
call_irq_callback_functions(
|
||||
&self.as_trap_frame(),
|
||||
TIMER_IRQ_NUM.load(Ordering::Relaxed) as usize,
|
||||
PrivilegeLevel::User,
|
||||
);
|
||||
}
|
||||
Trap::Interrupt(_) => todo!(),
|
||||
|
|
|
@ -13,11 +13,7 @@ pub(super) use trap::RawUserContext;
|
|||
pub use trap::TrapFrame;
|
||||
|
||||
use super::{cpu::context::CpuExceptionInfo, timer::TIMER_IRQ_NUM};
|
||||
use crate::{cpu_local_cell, irq::call_irq_callback_functions};
|
||||
|
||||
cpu_local_cell! {
|
||||
static IS_KERNEL_INTERRUPTED: bool = false;
|
||||
}
|
||||
use crate::{cpu::PrivilegeLevel, irq::call_irq_callback_functions};
|
||||
|
||||
/// Initializes interrupt handling on RISC-V.
|
||||
pub(crate) unsafe fn init() {
|
||||
|
@ -26,35 +22,28 @@ pub(crate) unsafe fn init() {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns true if this function is called within the context of an IRQ handler
|
||||
/// and the IRQ occurs while the CPU is executing in the kernel mode.
|
||||
/// Otherwise, it returns false.
|
||||
pub fn is_kernel_interrupted() -> bool {
|
||||
IS_KERNEL_INTERRUPTED.load()
|
||||
}
|
||||
|
||||
/// Handle traps (only from kernel).
|
||||
#[no_mangle]
|
||||
extern "C" fn trap_handler(f: &mut TrapFrame) {
|
||||
use riscv::register::scause::Trap;
|
||||
|
||||
match riscv::register::scause::read().cause() {
|
||||
Trap::Interrupt(interrupt) => {
|
||||
IS_KERNEL_INTERRUPTED.store(true);
|
||||
match interrupt {
|
||||
Interrupt::SupervisorTimer => {
|
||||
call_irq_callback_functions(f, TIMER_IRQ_NUM.load(Ordering::Relaxed) as usize);
|
||||
}
|
||||
Interrupt::SupervisorExternal => todo!(),
|
||||
Interrupt::SupervisorSoft => todo!(),
|
||||
_ => {
|
||||
panic!(
|
||||
Trap::Interrupt(interrupt) => match interrupt {
|
||||
Interrupt::SupervisorTimer => {
|
||||
call_irq_callback_functions(
|
||||
f,
|
||||
TIMER_IRQ_NUM.load(Ordering::Relaxed) as usize,
|
||||
PrivilegeLevel::Kernel,
|
||||
);
|
||||
}
|
||||
Interrupt::SupervisorExternal => todo!(),
|
||||
Interrupt::SupervisorSoft => todo!(),
|
||||
_ => {
|
||||
panic!(
|
||||
"cannot handle unknown supervisor interrupt: {interrupt:?}. trapframe: {f:#x?}.",
|
||||
);
|
||||
}
|
||||
}
|
||||
IS_KERNEL_INTERRUPTED.store(false);
|
||||
}
|
||||
},
|
||||
Trap::Exception(e) => {
|
||||
let stval = riscv::register::stval::read();
|
||||
panic!(
|
||||
|
|
|
@ -19,6 +19,7 @@ use x86_64::registers::{
|
|||
|
||||
use crate::{
|
||||
arch::trap::{RawUserContext, TrapFrame},
|
||||
cpu::PrivilegeLevel,
|
||||
irq::call_irq_callback_functions,
|
||||
mm::Vaddr,
|
||||
task::scheduler,
|
||||
|
@ -304,6 +305,7 @@ impl UserContextApiInternal for UserContext {
|
|||
call_irq_callback_functions(
|
||||
&self.as_trap_frame(),
|
||||
self.as_trap_frame().trap_num,
|
||||
PrivilegeLevel::User,
|
||||
);
|
||||
crate::arch::irq::enable_local();
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ use crate::{
|
|||
if_tdx_enabled,
|
||||
irq::{disable_local, enable_local},
|
||||
},
|
||||
cpu_local_cell,
|
||||
cpu::PrivilegeLevel,
|
||||
irq::call_irq_callback_functions,
|
||||
mm::{
|
||||
kspace::{KERNEL_PAGE_TABLE, LINEAR_MAPPING_BASE_VADDR, LINEAR_MAPPING_VADDR_RANGE},
|
||||
|
@ -49,10 +49,6 @@ cfg_if! {
|
|||
}
|
||||
}
|
||||
|
||||
cpu_local_cell! {
|
||||
static KERNEL_INTERRUPT_NESTED_LEVEL: u8 = 0;
|
||||
}
|
||||
|
||||
/// Trap frame of kernel interrupt
|
||||
///
|
||||
/// # Trap handler
|
||||
|
@ -138,13 +134,6 @@ pub(super) struct RawUserContext {
|
|||
pub(super) error_code: usize,
|
||||
}
|
||||
|
||||
/// Returns true if this function is called within the context of an IRQ handler
|
||||
/// and the IRQ occurs while the CPU is executing in the kernel mode.
|
||||
/// Otherwise, it returns false.
|
||||
pub fn is_kernel_interrupted() -> bool {
|
||||
KERNEL_INTERRUPT_NESTED_LEVEL.load() != 0
|
||||
}
|
||||
|
||||
/// Handle traps (only from kernel).
|
||||
#[no_mangle]
|
||||
extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
|
||||
|
@ -197,9 +186,7 @@ extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
|
|||
);
|
||||
}
|
||||
None => {
|
||||
KERNEL_INTERRUPT_NESTED_LEVEL.add_assign(1);
|
||||
call_irq_callback_functions(f, f.trap_num);
|
||||
KERNEL_INTERRUPT_NESTED_LEVEL.sub_assign(1);
|
||||
call_irq_callback_functions(f, f.trap_num, PrivilegeLevel::Kernel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,16 @@ impl TryFrom<usize> for CpuId {
|
|||
}
|
||||
}
|
||||
|
||||
/// The CPU privilege level: user mode or kernel mode.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(u8)]
|
||||
pub enum PrivilegeLevel {
|
||||
/// User mode.
|
||||
User = 0,
|
||||
/// Kernel mode.
|
||||
Kernel = 1,
|
||||
}
|
||||
|
||||
/// The number of CPUs.
|
||||
static mut NUM_CPUS: u32 = 1;
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ static BOTTOM_HALF_HANDLER_L2: Once<fn()> = Once::new();
|
|||
|
||||
pub(super) fn process() {
|
||||
match InterruptLevel::current() {
|
||||
InterruptLevel::L1 => process_l1(),
|
||||
InterruptLevel::L1(_) => process_l1(),
|
||||
InterruptLevel::L2 => process_l2(),
|
||||
_ => unreachable!("this function must have been call in interrupt context"),
|
||||
}
|
||||
|
|
|
@ -2,34 +2,30 @@
|
|||
|
||||
//! The interrupt level.
|
||||
|
||||
use crate::cpu_local_cell;
|
||||
|
||||
// Enter the scope of interrupt handling,
|
||||
// increasing the interrupt level by one.
|
||||
pub(super) fn enter<F: FnOnce()>(f: F) {
|
||||
INTERRUPT_LEVEL.add_assign(1);
|
||||
|
||||
f();
|
||||
|
||||
INTERRUPT_LEVEL.sub_assign(1);
|
||||
}
|
||||
use crate::{cpu::PrivilegeLevel, cpu_local_cell};
|
||||
|
||||
/// The current interrupt level on a CPU.
|
||||
///
|
||||
/// This type tracks the current nesting depth on the CPU
|
||||
/// where the code is executing.
|
||||
/// There are three levels:
|
||||
/// * Level 0 (the task context);
|
||||
/// * Level 1 (the interrupt context);
|
||||
/// * Level 2 (the interrupt context due to nested interrupts).
|
||||
///
|
||||
/// An `InterruptLevel` is specific to a single CPU
|
||||
/// and is meaningless when used by or sent to other CPUs,
|
||||
/// hence it is `!Send` and `!Sync`.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum InterruptLevel {
|
||||
/// Level 0 (the task context).
|
||||
L0 = 0,
|
||||
L0,
|
||||
/// Level 1 (the interrupt context).
|
||||
L1 = 1,
|
||||
///
|
||||
/// The intern value specifies the CPU privilege level of the interrupted code.
|
||||
L1(PrivilegeLevel),
|
||||
/// Level 2 (the interrupt context due to nested interrupts).
|
||||
L2 = 2,
|
||||
L2,
|
||||
}
|
||||
|
||||
impl !Send for InterruptLevel {}
|
||||
|
@ -38,15 +34,36 @@ impl !Sync for InterruptLevel {}
|
|||
impl InterruptLevel {
|
||||
/// Returns the current interrupt level of this CPU.
|
||||
pub fn current() -> Self {
|
||||
let level = INTERRUPT_LEVEL.load();
|
||||
// Parameters about the encoding of INTERRUPT_LEVEL
|
||||
const LEVEL_VAL_OFFSET: u8 = 1;
|
||||
const CPU_PRIV_MASK: u8 = 1 << 0;
|
||||
|
||||
let raw_level = INTERRUPT_LEVEL.load();
|
||||
let level = raw_level >> LEVEL_VAL_OFFSET;
|
||||
match level {
|
||||
0 => Self::L0,
|
||||
1 => Self::L1,
|
||||
1 => {
|
||||
let cpu_priv_at_irq = if (raw_level & CPU_PRIV_MASK) == 0 {
|
||||
PrivilegeLevel::Kernel
|
||||
} else {
|
||||
PrivilegeLevel::User
|
||||
};
|
||||
Self::L1(cpu_priv_at_irq)
|
||||
}
|
||||
2 => Self::L2,
|
||||
_ => unreachable!("level must between 0 and 2 (inclusive)"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the interrupt level as an integer between 0 and 2 (inclusive).
|
||||
pub fn as_u8(&self) -> u8 {
|
||||
match self {
|
||||
Self::L0 => 0,
|
||||
Self::L1(_) => 1,
|
||||
Self::L2 => 2,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the CPU is currently in the task context (level 0).
|
||||
pub fn is_task_context(&self) -> bool {
|
||||
*self == Self::L0
|
||||
|
@ -54,10 +71,50 @@ impl InterruptLevel {
|
|||
|
||||
/// Checks if the CPU is currently in the interrupt context (level 1 or 2).
|
||||
pub fn is_interrupt_context(&self) -> bool {
|
||||
*self == Self::L1 || *self == Self::L2
|
||||
matches!(self, Self::L1(_) | Self::L2)
|
||||
}
|
||||
}
|
||||
|
||||
/// Enters the scope of interrupt handling,
|
||||
/// increasing the interrupt level by one.
|
||||
///
|
||||
/// The `cpu_priv_at_irq` argument specifies the CPU privilege level of
|
||||
/// the code interrupted by the IRQ.
|
||||
pub(super) fn enter<F: FnOnce()>(f: F, cpu_priv_at_irq: PrivilegeLevel) {
|
||||
let increment = {
|
||||
let bit_0 = match cpu_priv_at_irq {
|
||||
PrivilegeLevel::Kernel => 0,
|
||||
PrivilegeLevel::User => 1,
|
||||
};
|
||||
let bit_1 = 0b10;
|
||||
bit_1 | bit_0
|
||||
};
|
||||
INTERRUPT_LEVEL.add_assign(increment);
|
||||
|
||||
f();
|
||||
|
||||
INTERRUPT_LEVEL.sub_assign(increment);
|
||||
}
|
||||
|
||||
cpu_local_cell! {
|
||||
/// The interrupt level of the current IRQ.
|
||||
///
|
||||
/// We pack two pieces of information into a single byte:
|
||||
/// 1. The current interrupt level (bit 1 - 7);
|
||||
/// 2. The CPU privilege level of the code interrupted by the IRQ (bit 0).
|
||||
///
|
||||
/// More specifically,
|
||||
/// the encoding of this byte is summarized in the table below.
|
||||
///
|
||||
/// | Values | Meaning |
|
||||
/// |----------|---------------------|
|
||||
/// | `0b00_0` | L0 |
|
||||
/// | `0b01_0` | L1 from kernel |
|
||||
/// | `0b01_1` | L1 from user |
|
||||
/// | `0b10_0` | L2 (L1 from kernel) |
|
||||
/// | `0b10_1` | L2 (L1 from user) |
|
||||
///
|
||||
/// This compact encoding allows us to update this value
|
||||
/// in a single arithematic operation (see `enter`).
|
||||
static INTERRUPT_LEVEL: u8 = 0;
|
||||
}
|
||||
|
|
|
@ -81,11 +81,18 @@ pub use guard::{disable_local, DisabledLocalIrqGuard};
|
|||
pub use level::InterruptLevel;
|
||||
pub use top_half::{IrqCallbackFunction, IrqLine};
|
||||
|
||||
use crate::arch::trap::TrapFrame;
|
||||
use crate::{arch::trap::TrapFrame, cpu::PrivilegeLevel};
|
||||
|
||||
pub(crate) fn call_irq_callback_functions(trap_frame: &TrapFrame, irq_num: usize) {
|
||||
level::enter(move || {
|
||||
top_half::process(trap_frame, irq_num);
|
||||
bottom_half::process();
|
||||
});
|
||||
pub(crate) fn call_irq_callback_functions(
|
||||
trap_frame: &TrapFrame,
|
||||
irq_num: usize,
|
||||
cpu_priv_at_irq: PrivilegeLevel,
|
||||
) {
|
||||
level::enter(
|
||||
move || {
|
||||
top_half::process(trap_frame, irq_num);
|
||||
bottom_half::process();
|
||||
},
|
||||
cpu_priv_at_irq,
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue