diff --git a/src/framework/jinux-frame/src/arch/x86/cpu.rs b/src/framework/jinux-frame/src/arch/x86/cpu.rs index 1642d7673..2792b3e08 100644 --- a/src/framework/jinux-frame/src/arch/x86/cpu.rs +++ b/src/framework/jinux-frame/src/arch/x86/cpu.rs @@ -90,7 +90,7 @@ impl UserContextApiInternal for UserContext { call_irq_callback_functions(&self.into_trap_frame()); } - crate::arch::irq::enable_interrupts(); + crate::arch::irq::enable_local(); if self.user_context.trap_num as u16 != SYSCALL_TRAPNUM { self.trap_information = TrapInformation { cr2: unsafe { x86::controlregs::cr2() }, diff --git a/src/framework/jinux-frame/src/arch/x86/irq.rs b/src/framework/jinux-frame/src/arch/x86/irq.rs index 555f9e697..d4e94f1cc 100644 --- a/src/framework/jinux-frame/src/arch/x86/irq.rs +++ b/src/framework/jinux-frame/src/arch/x86/irq.rs @@ -20,6 +20,14 @@ pub(crate) fn init() { IRQ_LIST.call_once(|| list); } -pub(crate) fn enable_interrupts() { +pub(crate) fn enable_local() { x86_64::instructions::interrupts::enable(); } + +pub(crate) fn disable_local() { + x86_64::instructions::interrupts::disable(); +} + +pub(crate) fn is_local_enabled() -> bool { + x86_64::instructions::interrupts::are_enabled() +} diff --git a/src/framework/jinux-frame/src/cpu.rs b/src/framework/jinux-frame/src/cpu.rs index cdc4170ee..4af4bdb99 100644 --- a/src/framework/jinux-frame/src/cpu.rs +++ b/src/framework/jinux-frame/src/cpu.rs @@ -1,8 +1,100 @@ //! CPU. +use crate::trap::disable_local; +use core::cell::UnsafeCell; +use core::ops::Deref; + cfg_if::cfg_if! { if #[cfg(target_arch = "x86_64")]{ pub use trapframe::GeneralRegs; pub use crate::arch::x86::cpu::*; } } + +/// Defines a CPU-local variable. +/// +/// # Example +/// +/// ```rust +/// use crate::cpu_local; +/// use core::cell::RefCell; +/// +/// cpu_local! { +/// static FOO: RefCell = RefCell::new(1); +/// +/// #[allow(unused)] +/// pub static BAR: RefCell = RefCell::new(1.0); +/// } +/// CpuLocal::borrow_with(&FOO, |val| { +/// println!("FOO VAL: {:?}", *val); +/// }) +/// +/// ``` +#[macro_export] +macro_rules! cpu_local { + // empty + () => {}; + + // multiple declarations + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => { + $(#[$attr])* $vis static $name: CpuLocal<$t> = unsafe { CpuLocal::new($init) }; + crate::cpu_local!($($rest)*); + }; + + // single declaration + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( + // TODO: reimplement cpu-local variable to support multi-core + $(#[$attr])* $vis static $name: CpuLocal<$t> = CpuLocal::new($init); + ); +} + +/// CPU-local objects. +/// +/// A CPU-local object only gives you immutable references to the underlying value. +/// To mutate the value, one can use atomic values (e.g., `AtomicU32`) or internally mutable +/// objects (e.g., `RefCell`). +/// +/// The `CpuLocal` can be used directly. +/// Otherwise, the `CpuLocal` must be used through `CpuLocal::borrow_with`. +/// +/// TODO: re-implement `CpuLocal` +pub struct CpuLocal(UnsafeCell); + +// Safety. At any given time, only one task can access the inner value T of a cpu-local variable. +unsafe impl Sync for CpuLocal {} + +impl CpuLocal { + /// Initialize CPU-local object + /// Developer cannot construct a valid CpuLocal object arbitrarily + pub const unsafe fn new(val: T) -> Self { + Self(UnsafeCell::new(val)) + } + + /// Borrow an immutable reference to the underlying value and feed it to a closure. + /// + /// During the execution of the closure, local IRQs are disabled. This ensures that + /// the CPU-local object is only accessed by the current task or IRQ handler. + /// As local IRQs are disabled, one should keep the closure as short as possible. + pub fn borrow_with U>(this: &Self, f: F) -> U { + // FIXME: implement disable preemption + // Disable interrupts when accessing cpu-local variable + let _guard = disable_local(); + // Safety. Now that the local IRQs are disabled, this CPU-local object can only be + // accessed by the current task/thread. So it is safe to get its immutable reference + // regardless of whether `T` implements `Sync` or not. + let val_ref = unsafe { this.do_borrow() }; + f(val_ref) + } + + unsafe fn do_borrow(&self) -> &T { + &*self.0.get() + } +} + +impl Deref for CpuLocal { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.0.get() } + } +} diff --git a/src/framework/jinux-frame/src/lib.rs b/src/framework/jinux-frame/src/lib.rs index 19b5a1f12..6dbc55618 100644 --- a/src/framework/jinux-frame/src/lib.rs +++ b/src/framework/jinux-frame/src/lib.rs @@ -29,6 +29,7 @@ pub mod user; mod util; pub mod vm; +pub use self::cpu::CpuLocal; pub use self::error::Error; pub use self::prelude::Result; use alloc::vec::Vec; diff --git a/src/framework/jinux-frame/src/sync/irq.rs b/src/framework/jinux-frame/src/sync/irq.rs deleted file mode 100644 index e68468c92..000000000 --- a/src/framework/jinux-frame/src/sync/irq.rs +++ /dev/null @@ -1,95 +0,0 @@ -use super::percpu::PerCpu; -use crate::cpu_local; -use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; -use x86_64::instructions::interrupts; - -/// Disable all IRQs on the current CPU (i.e., locally). -/// -/// This function returns a guard object, which will automatically enable local IRQs again when -/// it is dropped. This function works correctly even when it is called in a _nested_ way. -/// The local IRQs shall only be re-enabled when the most outer guard is dropped. -/// -/// This function can play nicely with `SpinLock` as the type uses this function internally. -/// One can invoke this function even after acquiring a spin lock. And the reversed order is also ok. -/// -/// # Example -/// -/// ``rust -/// use jinux_frame::irq; -/// -/// { -/// let _ = irq::disable_local(); -/// todo!("do something when irqs are disabled"); -/// } -/// ``` -#[must_use] -pub fn disable_local() -> DisabledLocalIrqGuard { - DisabledLocalIrqGuard::new() -} - -/// A guard for disabled local IRQs. -pub struct DisabledLocalIrqGuard { - // Having a private field prevents user from constructing values of this type directly. - private: (), -} - -impl !Send for DisabledLocalIrqGuard {} - -cpu_local! { - static IRQ_OFF_COUNT: IrqInfo = IrqInfo::new(); -} - -impl DisabledLocalIrqGuard { - fn new() -> Self { - IRQ_OFF_COUNT.borrow().inc(); - Self { private: () } - } -} - -impl Drop for DisabledLocalIrqGuard { - fn drop(&mut self) { - IRQ_OFF_COUNT.borrow().dec(); - } -} - -#[derive(Debug, Default)] -struct IrqInfo { - // number of interrupt counterpart - off_num: AtomicU32, - // interrupt state before calling dec()/inc() - interrupt_enable: AtomicBool, -} - -impl IrqInfo { - const fn new() -> Self { - Self { - off_num: AtomicU32::new(0), - interrupt_enable: AtomicBool::new(false), - } - } - - fn inc(&self) { - let enabled = interrupts::are_enabled(); - let off_num = self.off_num.load(Ordering::Relaxed); - if off_num == 0 { - self.interrupt_enable.store(enabled, Ordering::Relaxed); - } - if enabled { - interrupts::disable(); - } - self.off_num.fetch_add(1, Ordering::Relaxed); - } - - fn dec(&self) { - let off_num = self.off_num.load(Ordering::Relaxed); - - if off_num < 1 { - // disable_interrupt/inc() should be called before enable_interrupt/dec() - panic!("The enable_interrupt and disable_interrupt are the counterpart"); - } - let update_num = self.off_num.fetch_sub(1, Ordering::Relaxed) - 1; - if update_num == 0 && self.interrupt_enable.load(Ordering::Relaxed) { - interrupts::enable(); - } - } -} diff --git a/src/framework/jinux-frame/src/sync/mod.rs b/src/framework/jinux-frame/src/sync/mod.rs index ea9720b7c..9a416987b 100644 --- a/src/framework/jinux-frame/src/sync/mod.rs +++ b/src/framework/jinux-frame/src/sync/mod.rs @@ -1,9 +1,11 @@ mod atomic_bits; +mod mutex; mod rcu; mod spin; mod wait; pub use self::atomic_bits::AtomicBits; +pub use self::mutex::{Mutex, MutexGuard}; pub use self::rcu::{pass_quiescent_state, OwnerPtr, Rcu, RcuReadGuard, RcuReclaimer}; pub use self::spin::{SpinLock, SpinLockGuard}; pub use self::wait::WaitQueue; diff --git a/src/framework/jinux-frame/src/sync/mutex.rs b/src/framework/jinux-frame/src/sync/mutex.rs new file mode 100644 index 000000000..bb0377183 --- /dev/null +++ b/src/framework/jinux-frame/src/sync/mutex.rs @@ -0,0 +1,60 @@ +use super::spin::{SpinLock, SpinLockGuard}; +use core::ops::{Deref, DerefMut}; + +use core::fmt; + +pub struct Mutex { + inner: SpinLock, +} + +impl Mutex { + #[inline(always)] + pub const fn new(val: T) -> Self { + Self { + inner: SpinLock::new(val), + } + } + + pub fn lock(&self) -> MutexGuard { + MutexGuard { + lock: self.inner.lock(), + } + } +} + +impl fmt::Debug for Mutex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.inner, f) + } +} + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +pub struct MutexGuard<'a, T> { + lock: SpinLockGuard<'a, T>, +} + +impl<'a, T> Deref for MutexGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + self.lock.deref() + } +} + +impl<'a, T> DerefMut for MutexGuard<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.lock.deref_mut() + } +} + +impl<'a, T: fmt::Debug> fmt::Debug for MutexGuard<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<'a, T> !Send for MutexGuard<'a, T> {} + +unsafe impl Sync for MutexGuard<'_, T> {} diff --git a/src/framework/jinux-frame/src/sync/percpu.rs b/src/framework/jinux-frame/src/sync/percpu.rs deleted file mode 100644 index 54a302905..000000000 --- a/src/framework/jinux-frame/src/sync/percpu.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::sync::disable_local; -use core::cell::UnsafeCell; - -/// Defines a CPU-local variable. -/// # Example -/// -/// ```rust -/// use crate::cpu_local; -/// use core::cell::RefCell; -/// { -/// cpu_local! { -/// static FOO: RefCell = RefCell::new(1); -/// -/// #[allow(unused)] -/// pub static BAR: RefCell = RefCell::new(1.0); -/// } -/// -/// FOO.borrow_with(|val| { -/// println!("FOO VAL: {:?}", *val); -/// }); -/// } -/// ``` -#[macro_export] -macro_rules! cpu_local { - // empty - () => {}; - - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }; $($rest:tt)*) => { - $(#[$attr])* $vis static $name: PerCpu<$t> = PerCpu::new(const $init); - crate::cpu_local!($($rest)*); - }; - - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }) => ( - $(#[$attr])* $vis static $name: PerCpu<$t> = PerCpu::new(const $init); - ); - - // multiple declarations - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => { - $(#[$attr])* $vis static $name: PerCpu<$t> = PerCpu::new($init); - crate::cpu_local!($($rest)*); - }; - - // single declaration - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( - // TODO: reimplement per-cpu variable to support multi-core - $(#[$attr])* $vis static $name: PerCpu<$t> = PerCpu::new($init); - ); -} - -/// Per-CPU objects. -/// -/// A per-CPU object only gives you immutable references to the underlying value. -/// To mutate the value, one can use atomic values (e.g., `AtomicU32`) or internally mutable -/// objects (e.g., `RefCell`). -/// -/// TODO: reimplement PerCpu inner UnsafeCell with access function as thread_local! macro, -/// because different CPUs access static per-cpu variables in different positions. -pub struct PerCpu(UnsafeCell); -// safety -unsafe impl Sync for PerCpu {} - -impl PerCpu { - /// Initialize per-CPU object - /// unsafe - pub const fn new(val: T) -> Self { - Self(UnsafeCell::new(val)) - } - /// Borrow an immutable reference to the underlying value and feed it to a closure. - /// - /// During the execution of the closure, local IRQs are disabled. This ensures that - /// the per-CPU object is only accessed by the current task or IRQ handler. - /// As local IRQs are disabled, one should keep the closure as short as possible. - pub fn borrow_with U>(&self, f: F) -> U { - // FIXME: implement disable preemption - // Disable interrupts when accessing per-cpu variable - let _guard = disable_local(); - // Safety. Now that the local IRQs are disabled, this per-CPU object can only be - // accessed by the current task/thread. So it is safe to get its immutable reference - // regardless of whether `T` implements `Sync` or not. - let val_ref = unsafe { self.do_borrow() }; - f(val_ref) - } - - unsafe fn do_borrow(&self) -> &T { - &*self.0.get() - } -} - -impl PerCpu { - /// Gets an immutable reference to the value inside the per-CPU object. - pub fn borrow(&self) -> &T { - // Safety. Since the value of `T` is `Sync`, it is ok for multiple tasks or IRQ handlers - // executing on the current CPU to have immutable references. - unsafe { self.do_borrow() } - } -} diff --git a/src/framework/jinux-frame/src/sync/spin.rs b/src/framework/jinux-frame/src/sync/spin.rs index 2a09b5239..17b576146 100644 --- a/src/framework/jinux-frame/src/sync/spin.rs +++ b/src/framework/jinux-frame/src/sync/spin.rs @@ -1,13 +1,10 @@ use core::cell::UnsafeCell; -use core::sync::atomic::Ordering; -use core::{ - ops::{Deref, DerefMut}, - sync::atomic::AtomicBool, -}; - -use crate::sync::disable_local; -use crate::sync::irq::DisabledLocalIrqGuard; use core::fmt; +use core::ops::{Deref, DerefMut}; +use core::sync::atomic::{AtomicBool, Ordering}; + +use crate::trap::disable_local; +use crate::trap::DisabledLocalIrqGuard; /// A spin lock. pub struct SpinLock { @@ -31,21 +28,35 @@ impl SpinLock { pub fn lock(&self) -> SpinLockGuard { // FIXME: add disable_preemption let guard = disable_local(); - self.access_lock(); + self.acquire_lock(); SpinLockGuard { lock: &self, irq_guard: guard, } } + /// Try Acquire the spin lock immedidately. + pub fn try_lock(&self) -> Option> { + // FIXME: add disable_preemption + let irq_guard = disable_local(); + if self.try_acquire_lock() { + let lock_guard = SpinLockGuard { + lock: &self, + irq_guard, + }; + return Some(lock_guard); + } + return None; + } + /// Access the spin lock, otherwise busy waiting - fn access_lock(&self) { - while !self.try_access_lock() { + fn acquire_lock(&self) { + while !self.try_acquire_lock() { core::hint::spin_loop(); } } - fn try_access_lock(&self) -> bool { + fn try_acquire_lock(&self) -> bool { self.lock .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) .is_ok() @@ -98,7 +109,6 @@ impl<'a, T: fmt::Debug> fmt::Debug for SpinLockGuard<'a, T> { } } -// SpinLockGuard cannot be sent between tasks/threads impl<'a, T> !Send for SpinLockGuard<'a, T> {} // Safety. SpinLockGuard can be shared between tasks/threads in same CPU. diff --git a/src/framework/jinux-frame/src/trap/irq.rs b/src/framework/jinux-frame/src/trap/irq.rs index 18f69c31b..807160784 100644 --- a/src/framework/jinux-frame/src/trap/irq.rs +++ b/src/framework/jinux-frame/src/trap/irq.rs @@ -1,10 +1,13 @@ +use crate::arch::irq; use crate::arch::irq::{IRQ_LIST, NOT_USING_IRQ}; +use crate::cpu::CpuLocal; +use crate::cpu_local; +use crate::util::recycle_allocator::RecycleAllocator; use crate::{prelude::*, Error}; -use crate::util::recycle_allocator::RecycleAllocator; use core::fmt::Debug; +use core::sync::atomic::{AtomicBool, AtomicU32, Ordering::Relaxed}; use spin::{Mutex, MutexGuard}; -// use crate::sync::{Mutex, MutexGuard}; use trapframe::TrapFrame; pub fn allocate_irq() -> Result { @@ -167,3 +170,88 @@ impl Drop for IrqCallbackHandle { ID_ALLOCATOR.lock().dealloc(self.id); } } + +/// Disable all IRQs on the current CPU (i.e., locally). +/// +/// This function returns a guard object, which will automatically enable local IRQs again when +/// it is dropped. This function works correctly even when it is called in a _nested_ way. +/// The local IRQs shall only be re-enabled when the most outer guard is dropped. +/// +/// This function can play nicely with `SpinLock` as the type uses this function internally. +/// One can invoke this function even after acquiring a spin lock. And the reversed order is also ok. +/// +/// # Example +/// +/// ``rust +/// use jinux_frame::irq; +/// +/// { +/// let _ = irq::disable_local(); +/// todo!("do something when irqs are disabled"); +/// } +/// ``` +#[must_use] +pub fn disable_local() -> DisabledLocalIrqGuard { + DisabledLocalIrqGuard::new() +} + +/// A guard for disabled local IRQs. +pub struct DisabledLocalIrqGuard { + // Having a private field prevents user from constructing values of this type directly. + private: (), +} + +impl !Send for DisabledLocalIrqGuard {} + +cpu_local! { + static IRQ_OFF_COUNT: IrqInfo = IrqInfo::new(); +} + +impl DisabledLocalIrqGuard { + fn new() -> Self { + IRQ_OFF_COUNT.inc(); + Self { private: () } + } +} + +impl Drop for DisabledLocalIrqGuard { + fn drop(&mut self) { + IRQ_OFF_COUNT.dec(); + } +} + +#[derive(Debug, Default)] +struct IrqInfo { + // interrupt disabling level + level: AtomicU32, + // interrupt state before calling dec()/inc() + is_irq_enabled: AtomicBool, +} + +impl IrqInfo { + const fn new() -> Self { + Self { + level: AtomicU32::new(0), + is_irq_enabled: AtomicBool::new(false), + } + } + + fn inc(&self) { + let enabled = irq::is_local_enabled(); + let level = self.level.load(Relaxed); + if level == 0 { + self.is_irq_enabled.store(enabled, Relaxed); + } + if enabled { + irq::disable_local(); + } + self.level.fetch_add(1, Relaxed); + } + + fn dec(&self) { + let level = self.level.fetch_sub(1, Relaxed) - 1; + if level == 0 && self.is_irq_enabled.load(Relaxed) { + irq::enable_local(); + } + } +} diff --git a/src/framework/jinux-frame/src/trap/mod.rs b/src/framework/jinux-frame/src/trap/mod.rs index b26136172..8bc6fc501 100644 --- a/src/framework/jinux-frame/src/trap/mod.rs +++ b/src/framework/jinux-frame/src/trap/mod.rs @@ -2,7 +2,7 @@ mod handler; mod irq; pub(crate) use self::handler::call_irq_callback_functions; -pub use self::irq::{allocate_irq, IrqAllocateHandle}; +pub use self::irq::{allocate_irq, disable_local, DisabledLocalIrqGuard, IrqAllocateHandle}; pub(crate) use self::irq::{allocate_target_irq, IrqCallbackHandle, IrqLine}; pub use trapframe::TrapFrame; diff --git a/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs b/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs index a535ee849..4b354c783 100644 --- a/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs +++ b/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs @@ -1,7 +1,6 @@ use crate::prelude::*; use alloc::string::String; use core::time::Duration; -use spin::MutexGuard; use super::{InodeMode, InodeType, Metadata, Vnode, NAME_MAX}; diff --git a/src/services/libs/jinux-std/src/prelude.rs b/src/services/libs/jinux-std/src/prelude.rs index 4f5dc8d5a..049cc5bfe 100644 --- a/src/services/libs/jinux-std/src/prelude.rs +++ b/src/services/libs/jinux-std/src/prelude.rs @@ -15,11 +15,12 @@ pub(crate) use alloc::vec::Vec; pub(crate) use bitflags::bitflags; pub(crate) use core::ffi::CStr; pub(crate) use jinux_frame::config::PAGE_SIZE; +pub(crate) use jinux_frame::sync::{Mutex, MutexGuard}; pub(crate) use jinux_frame::vm::Vaddr; pub(crate) use jinux_frame::{print, println}; pub(crate) use log::{debug, error, info, trace, warn}; pub(crate) use pod::Pod; -pub(crate) use spin::{Mutex, RwLock}; +pub(crate) use spin::RwLock; /// return current process #[macro_export] diff --git a/src/services/libs/jinux-std/src/vm/vmo/mod.rs b/src/services/libs/jinux-std/src/vm/vmo/mod.rs index 328ad0ef4..9717d80fe 100644 --- a/src/services/libs/jinux-std/src/vm/vmo/mod.rs +++ b/src/services/libs/jinux-std/src/vm/vmo/mod.rs @@ -15,7 +15,6 @@ mod static_cap; pub use options::{VmoChildOptions, VmoOptions}; pub use pager::Pager; -use spin::Mutex; /// Virtual Memory Objects (VMOs) are a type of capability that represents a /// range of memory pages.