mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-18 03:56:42 +00:00
Add mutex primitive and replace mutex in Jinux
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
5277886b5d
commit
888853a6de
@ -90,7 +90,7 @@ impl UserContextApiInternal for UserContext {
|
|||||||
call_irq_callback_functions(&self.into_trap_frame());
|
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 {
|
if self.user_context.trap_num as u16 != SYSCALL_TRAPNUM {
|
||||||
self.trap_information = TrapInformation {
|
self.trap_information = TrapInformation {
|
||||||
cr2: unsafe { x86::controlregs::cr2() },
|
cr2: unsafe { x86::controlregs::cr2() },
|
||||||
|
@ -20,6 +20,14 @@ pub(crate) fn init() {
|
|||||||
IRQ_LIST.call_once(|| list);
|
IRQ_LIST.call_once(|| list);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn enable_interrupts() {
|
pub(crate) fn enable_local() {
|
||||||
x86_64::instructions::interrupts::enable();
|
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()
|
||||||
|
}
|
||||||
|
@ -1,8 +1,100 @@
|
|||||||
//! CPU.
|
//! CPU.
|
||||||
|
|
||||||
|
use crate::trap::disable_local;
|
||||||
|
use core::cell::UnsafeCell;
|
||||||
|
use core::ops::Deref;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(target_arch = "x86_64")]{
|
if #[cfg(target_arch = "x86_64")]{
|
||||||
pub use trapframe::GeneralRegs;
|
pub use trapframe::GeneralRegs;
|
||||||
pub use crate::arch::x86::cpu::*;
|
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<u32> = RefCell::new(1);
|
||||||
|
///
|
||||||
|
/// #[allow(unused)]
|
||||||
|
/// pub static BAR: RefCell<f32> = 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<T: Sync>` can be used directly.
|
||||||
|
/// Otherwise, the `CpuLocal<T>` must be used through `CpuLocal::borrow_with`.
|
||||||
|
///
|
||||||
|
/// TODO: re-implement `CpuLocal`
|
||||||
|
pub struct CpuLocal<T>(UnsafeCell<T>);
|
||||||
|
|
||||||
|
// Safety. At any given time, only one task can access the inner value T of a cpu-local variable.
|
||||||
|
unsafe impl<T> Sync for CpuLocal<T> {}
|
||||||
|
|
||||||
|
impl<T> CpuLocal<T> {
|
||||||
|
/// 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, F: FnOnce(&T) -> 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<T: Sync> Deref for CpuLocal<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
unsafe { &*self.0.get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -29,6 +29,7 @@ pub mod user;
|
|||||||
mod util;
|
mod util;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
||||||
|
pub use self::cpu::CpuLocal;
|
||||||
pub use self::error::Error;
|
pub use self::error::Error;
|
||||||
pub use self::prelude::Result;
|
pub use self::prelude::Result;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,11 @@
|
|||||||
mod atomic_bits;
|
mod atomic_bits;
|
||||||
|
mod mutex;
|
||||||
mod rcu;
|
mod rcu;
|
||||||
mod spin;
|
mod spin;
|
||||||
mod wait;
|
mod wait;
|
||||||
|
|
||||||
pub use self::atomic_bits::AtomicBits;
|
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::rcu::{pass_quiescent_state, OwnerPtr, Rcu, RcuReadGuard, RcuReclaimer};
|
||||||
pub use self::spin::{SpinLock, SpinLockGuard};
|
pub use self::spin::{SpinLock, SpinLockGuard};
|
||||||
pub use self::wait::WaitQueue;
|
pub use self::wait::WaitQueue;
|
||||||
|
60
src/framework/jinux-frame/src/sync/mutex.rs
Normal file
60
src/framework/jinux-frame/src/sync/mutex.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use super::spin::{SpinLock, SpinLockGuard};
|
||||||
|
use core::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
pub struct Mutex<T> {
|
||||||
|
inner: SpinLock<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Mutex<T> {
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn new(val: T) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: SpinLock::new(val),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lock(&self) -> MutexGuard<T> {
|
||||||
|
MutexGuard {
|
||||||
|
lock: self.inner.lock(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: fmt::Debug> fmt::Debug for Mutex<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&self.inner, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: Send> Send for Mutex<T> {}
|
||||||
|
unsafe impl<T: Send> Sync for Mutex<T> {}
|
||||||
|
|
||||||
|
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<T: Sync> Sync for MutexGuard<'_, T> {}
|
@ -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<u32> = RefCell::new(1);
|
|
||||||
///
|
|
||||||
/// #[allow(unused)]
|
|
||||||
/// pub static BAR: RefCell<f32> = 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<T>(UnsafeCell<T>);
|
|
||||||
// safety
|
|
||||||
unsafe impl<T> Sync for PerCpu<T> {}
|
|
||||||
|
|
||||||
impl<T> PerCpu<T> {
|
|
||||||
/// 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, F: FnOnce(&T) -> 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<T: Sync> PerCpu<T> {
|
|
||||||
/// 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() }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +1,10 @@
|
|||||||
use core::cell::UnsafeCell;
|
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::fmt;
|
||||||
|
use core::ops::{Deref, DerefMut};
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
use crate::trap::disable_local;
|
||||||
|
use crate::trap::DisabledLocalIrqGuard;
|
||||||
|
|
||||||
/// A spin lock.
|
/// A spin lock.
|
||||||
pub struct SpinLock<T> {
|
pub struct SpinLock<T> {
|
||||||
@ -31,21 +28,35 @@ impl<T> SpinLock<T> {
|
|||||||
pub fn lock(&self) -> SpinLockGuard<T> {
|
pub fn lock(&self) -> SpinLockGuard<T> {
|
||||||
// FIXME: add disable_preemption
|
// FIXME: add disable_preemption
|
||||||
let guard = disable_local();
|
let guard = disable_local();
|
||||||
self.access_lock();
|
self.acquire_lock();
|
||||||
SpinLockGuard {
|
SpinLockGuard {
|
||||||
lock: &self,
|
lock: &self,
|
||||||
irq_guard: guard,
|
irq_guard: guard,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try Acquire the spin lock immedidately.
|
||||||
|
pub fn try_lock(&self) -> Option<SpinLockGuard<T>> {
|
||||||
|
// 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
|
/// Access the spin lock, otherwise busy waiting
|
||||||
fn access_lock(&self) {
|
fn acquire_lock(&self) {
|
||||||
while !self.try_access_lock() {
|
while !self.try_acquire_lock() {
|
||||||
core::hint::spin_loop();
|
core::hint::spin_loop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_access_lock(&self) -> bool {
|
fn try_acquire_lock(&self) -> bool {
|
||||||
self.lock
|
self.lock
|
||||||
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
|
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
|
||||||
.is_ok()
|
.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> {}
|
impl<'a, T> !Send for SpinLockGuard<'a, T> {}
|
||||||
|
|
||||||
// Safety. SpinLockGuard can be shared between tasks/threads in same CPU.
|
// Safety. SpinLockGuard can be shared between tasks/threads in same CPU.
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
|
use crate::arch::irq;
|
||||||
use crate::arch::irq::{IRQ_LIST, NOT_USING_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::{prelude::*, Error};
|
||||||
|
|
||||||
use crate::util::recycle_allocator::RecycleAllocator;
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering::Relaxed};
|
||||||
use spin::{Mutex, MutexGuard};
|
use spin::{Mutex, MutexGuard};
|
||||||
// use crate::sync::{Mutex, MutexGuard};
|
|
||||||
use trapframe::TrapFrame;
|
use trapframe::TrapFrame;
|
||||||
|
|
||||||
pub fn allocate_irq() -> Result<IrqAllocateHandle> {
|
pub fn allocate_irq() -> Result<IrqAllocateHandle> {
|
||||||
@ -167,3 +170,88 @@ impl Drop for IrqCallbackHandle {
|
|||||||
ID_ALLOCATOR.lock().dealloc(self.id);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ mod handler;
|
|||||||
mod irq;
|
mod irq;
|
||||||
|
|
||||||
pub(crate) use self::handler::call_irq_callback_functions;
|
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(crate) use self::irq::{allocate_target_irq, IrqCallbackHandle, IrqLine};
|
||||||
pub use trapframe::TrapFrame;
|
pub use trapframe::TrapFrame;
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
use spin::MutexGuard;
|
|
||||||
|
|
||||||
use super::{InodeMode, InodeType, Metadata, Vnode, NAME_MAX};
|
use super::{InodeMode, InodeType, Metadata, Vnode, NAME_MAX};
|
||||||
|
|
||||||
|
@ -15,11 +15,12 @@ pub(crate) use alloc::vec::Vec;
|
|||||||
pub(crate) use bitflags::bitflags;
|
pub(crate) use bitflags::bitflags;
|
||||||
pub(crate) use core::ffi::CStr;
|
pub(crate) use core::ffi::CStr;
|
||||||
pub(crate) use jinux_frame::config::PAGE_SIZE;
|
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::vm::Vaddr;
|
||||||
pub(crate) use jinux_frame::{print, println};
|
pub(crate) use jinux_frame::{print, println};
|
||||||
pub(crate) use log::{debug, error, info, trace, warn};
|
pub(crate) use log::{debug, error, info, trace, warn};
|
||||||
pub(crate) use pod::Pod;
|
pub(crate) use pod::Pod;
|
||||||
pub(crate) use spin::{Mutex, RwLock};
|
pub(crate) use spin::RwLock;
|
||||||
|
|
||||||
/// return current process
|
/// return current process
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
@ -15,7 +15,6 @@ mod static_cap;
|
|||||||
|
|
||||||
pub use options::{VmoChildOptions, VmoOptions};
|
pub use options::{VmoChildOptions, VmoOptions};
|
||||||
pub use pager::Pager;
|
pub use pager::Pager;
|
||||||
use spin::Mutex;
|
|
||||||
|
|
||||||
/// Virtual Memory Objects (VMOs) are a type of capability that represents a
|
/// Virtual Memory Objects (VMOs) are a type of capability that represents a
|
||||||
/// range of memory pages.
|
/// range of memory pages.
|
||||||
|
Reference in New Issue
Block a user