Implement WriteIrqDisabled

This commit is contained in:
Ruihan Li 2024-12-03 10:51:11 +08:00 committed by Tate, Hongliang Tian
parent 29659dbc98
commit a260411a2a
7 changed files with 58 additions and 22 deletions

View File

@ -9,7 +9,7 @@ use core::{
sync::atomic::{AtomicBool, AtomicU64, AtomicU8, Ordering}, sync::atomic::{AtomicBool, AtomicU64, AtomicU8, Ordering},
}; };
use ostd::sync::{LocalIrqDisabled, RwLock, SpinLock, SpinLockGuard}; use ostd::sync::{LocalIrqDisabled, RwLock, SpinLock, SpinLockGuard, WriteIrqDisabled};
use smoltcp::{ use smoltcp::{
iface::Context, iface::Context,
socket::{tcp::State, udp::UdpMetadata, PollAt}, socket::{tcp::State, udp::UdpMetadata, PollAt},
@ -46,7 +46,7 @@ pub struct BoundSocketInner<T, E> {
iface: Arc<dyn Iface<E>>, iface: Arc<dyn Iface<E>>,
port: u16, port: u16,
socket: T, socket: T,
observer: RwLock<Weak<dyn SocketEventObserver>, LocalIrqDisabled>, observer: RwLock<Weak<dyn SocketEventObserver>, WriteIrqDisabled>,
events: AtomicU8, events: AtomicU8,
next_poll_at_ms: AtomicU64, next_poll_at_ms: AtomicU64,
} }
@ -232,8 +232,6 @@ impl<T: AnySocket, E> BoundSocket<T, E> {
/// ///
/// See also [`Self::set_observer`]. /// See also [`Self::set_observer`].
pub fn observer(&self) -> Weak<dyn SocketEventObserver> { pub fn observer(&self) -> Weak<dyn SocketEventObserver> {
// We never hold the write lock in IRQ handlers, so we don't need to disable IRQs when we
// get the read lock.
self.0.observer.read().clone() self.0.observer.read().clone()
} }

View File

@ -6,7 +6,7 @@ use aster_bigtcp::{
socket::{SocketEventObserver, SocketEvents}, socket::{SocketEventObserver, SocketEvents},
wire::IpEndpoint, wire::IpEndpoint,
}; };
use ostd::sync::LocalIrqDisabled; use ostd::sync::WriteIrqDisabled;
use takeable::Takeable; use takeable::Takeable;
use self::{bound::BoundDatagram, unbound::UnboundDatagram}; use self::{bound::BoundDatagram, unbound::UnboundDatagram};
@ -52,7 +52,7 @@ impl OptionSet {
pub struct DatagramSocket { pub struct DatagramSocket {
options: RwLock<OptionSet>, options: RwLock<OptionSet>,
inner: RwLock<Takeable<Inner>, LocalIrqDisabled>, inner: RwLock<Takeable<Inner>, WriteIrqDisabled>,
nonblocking: AtomicBool, nonblocking: AtomicBool,
pollee: Pollee, pollee: Pollee,
} }

View File

@ -3,7 +3,7 @@
use aster_bigtcp::{ use aster_bigtcp::{
errors::tcp::ListenError, iface::BindPortConfig, socket::UnboundTcpSocket, wire::IpEndpoint, errors::tcp::ListenError, iface::BindPortConfig, socket::UnboundTcpSocket, wire::IpEndpoint,
}; };
use ostd::sync::LocalIrqDisabled; use ostd::sync::WriteIrqDisabled;
use super::connected::ConnectedStream; use super::connected::ConnectedStream;
use crate::{ use crate::{
@ -17,7 +17,7 @@ pub struct ListenStream {
/// A bound socket held to ensure the TCP port cannot be released /// A bound socket held to ensure the TCP port cannot be released
bound_socket: BoundTcpSocket, bound_socket: BoundTcpSocket,
/// Backlog sockets listening at the local endpoint /// Backlog sockets listening at the local endpoint
backlog_sockets: RwLock<Vec<BacklogSocket>, LocalIrqDisabled>, backlog_sockets: RwLock<Vec<BacklogSocket>, WriteIrqDisabled>,
} }
impl ListenStream { impl ListenStream {

View File

@ -11,7 +11,7 @@ use connecting::{ConnResult, ConnectingStream};
use init::InitStream; use init::InitStream;
use listen::ListenStream; use listen::ListenStream;
use options::{Congestion, MaxSegment, NoDelay, WindowClamp}; use options::{Congestion, MaxSegment, NoDelay, WindowClamp};
use ostd::sync::{LocalIrqDisabled, PreemptDisabled, RwLockReadGuard, RwLockWriteGuard}; use ostd::sync::{PreemptDisabled, RwLockReadGuard, RwLockWriteGuard, WriteIrqDisabled};
use takeable::Takeable; use takeable::Takeable;
use util::TcpOptionSet; use util::TcpOptionSet;
@ -50,7 +50,7 @@ pub use self::util::CongestionControl;
pub struct StreamSocket { pub struct StreamSocket {
options: RwLock<OptionSet>, options: RwLock<OptionSet>,
state: RwLock<Takeable<State>, LocalIrqDisabled>, state: RwLock<Takeable<State>, WriteIrqDisabled>,
is_nonblocking: AtomicBool, is_nonblocking: AtomicBool,
pollee: Pollee, pollee: Pollee,
} }
@ -116,7 +116,7 @@ impl StreamSocket {
/// Ensures that the socket state is up to date and obtains a read lock on it. /// Ensures that the socket state is up to date and obtains a read lock on it.
/// ///
/// For a description of what "up-to-date" means, see [`Self::update_connecting`]. /// For a description of what "up-to-date" means, see [`Self::update_connecting`].
fn read_updated_state(&self) -> RwLockReadGuard<Takeable<State>, LocalIrqDisabled> { fn read_updated_state(&self) -> RwLockReadGuard<Takeable<State>, WriteIrqDisabled> {
loop { loop {
let state = self.state.read(); let state = self.state.read();
match state.as_ref() { match state.as_ref() {
@ -132,7 +132,7 @@ impl StreamSocket {
/// Ensures that the socket state is up to date and obtains a write lock on it. /// Ensures that the socket state is up to date and obtains a write lock on it.
/// ///
/// For a description of what "up-to-date" means, see [`Self::update_connecting`]. /// For a description of what "up-to-date" means, see [`Self::update_connecting`].
fn write_updated_state(&self) -> RwLockWriteGuard<Takeable<State>, LocalIrqDisabled> { fn write_updated_state(&self) -> RwLockWriteGuard<Takeable<State>, WriteIrqDisabled> {
self.update_connecting().1 self.update_connecting().1
} }
@ -149,7 +149,7 @@ impl StreamSocket {
&self, &self,
) -> ( ) -> (
RwLockWriteGuard<OptionSet, PreemptDisabled>, RwLockWriteGuard<OptionSet, PreemptDisabled>,
RwLockWriteGuard<Takeable<State>, LocalIrqDisabled>, RwLockWriteGuard<Takeable<State>, WriteIrqDisabled>,
) { ) {
// Hold the lock in advance to avoid race conditions. // Hold the lock in advance to avoid race conditions.
let mut options = self.options.write(); let mut options = self.options.write();

View File

@ -5,13 +5,17 @@ use crate::{
trap::{disable_local, DisabledLocalIrqGuard}, trap::{disable_local, DisabledLocalIrqGuard},
}; };
/// A guardian that denotes the guard behavior for holding the spin lock. /// A guardian that denotes the guard behavior for holding a lock.
pub trait Guardian { pub trait Guardian {
/// The guard type. /// The guard type for holding a spin lock or a write lock.
type Guard: GuardTransfer; type Guard: GuardTransfer;
/// The guard type for holding a read lock.
type ReadGuard: GuardTransfer;
/// Creates a new guard. /// Creates a new guard.
fn guard() -> Self::Guard; fn guard() -> Self::Guard;
/// Creates a new read guard.
fn read_guard() -> Self::ReadGuard;
} }
/// The Guard can be transferred atomically. /// The Guard can be transferred atomically.
@ -25,21 +29,25 @@ pub trait GuardTransfer {
fn transfer_to(&mut self) -> Self; fn transfer_to(&mut self) -> Self;
} }
/// A guardian that disables preemption while holding the spin lock. /// A guardian that disables preemption while holding a lock.
pub struct PreemptDisabled; pub struct PreemptDisabled;
impl Guardian for PreemptDisabled { impl Guardian for PreemptDisabled {
type Guard = DisabledPreemptGuard; type Guard = DisabledPreemptGuard;
type ReadGuard = DisabledPreemptGuard;
fn guard() -> Self::Guard { fn guard() -> Self::Guard {
disable_preempt() disable_preempt()
} }
fn read_guard() -> Self::Guard {
disable_preempt()
}
} }
/// A guardian that disables IRQs while holding the spin lock. /// A guardian that disables IRQs while holding a lock.
/// ///
/// This guardian would incur a certain time overhead over /// This guardian would incur a certain time overhead over
/// [`PreemptDisabled']. So prefer avoiding using this guardian when /// [`PreemptDisabled`]. So prefer avoiding using this guardian when
/// IRQ handlers are allowed to get executed while holding the /// IRQ handlers are allowed to get executed while holding the
/// lock. For example, if a lock is never used in the interrupt /// lock. For example, if a lock is never used in the interrupt
/// context, then it is ok not to use this guardian in the process context. /// context, then it is ok not to use this guardian in the process context.
@ -47,8 +55,38 @@ pub struct LocalIrqDisabled;
impl Guardian for LocalIrqDisabled { impl Guardian for LocalIrqDisabled {
type Guard = DisabledLocalIrqGuard; type Guard = DisabledLocalIrqGuard;
type ReadGuard = DisabledLocalIrqGuard;
fn guard() -> Self::Guard { fn guard() -> Self::Guard {
disable_local() disable_local()
} }
fn read_guard() -> Self::Guard {
disable_local()
}
}
/// A guardian that disables IRQs while holding a write lock.
///
/// This guardian should only be used for a [`RwLock`]. Using it with a [`SpinLock`] will behave in
/// the same way as using [`LocalIrqDisabled`].
///
/// When using this guardian with a [`RwLock`], holding the read lock will only disable preemption,
/// but holding a write lock will disable local IRQs. The user must ensure that the IRQ handlers
/// never take the write lock, so we can take the read lock without disabling IRQs, but we are
/// still free of deadlock even if the IRQ handlers are triggered in the middle.
///
/// [`RwLock`]: super::RwLock
/// [`SpinLock`]: super::SpinLock
pub struct WriteIrqDisabled;
impl Guardian for WriteIrqDisabled {
type Guard = DisabledLocalIrqGuard;
type ReadGuard = DisabledPreemptGuard;
fn guard() -> Self::Guard {
disable_local()
}
fn read_guard() -> Self::ReadGuard {
disable_preempt()
}
} }

View File

@ -15,7 +15,7 @@ mod wait;
// pub use self::rcu::{pass_quiescent_state, OwnerPtr, Rcu, RcuReadGuard, RcuReclaimer}; // pub use self::rcu::{pass_quiescent_state, OwnerPtr, Rcu, RcuReadGuard, RcuReclaimer};
pub(crate) use self::guard::GuardTransfer; pub(crate) use self::guard::GuardTransfer;
pub use self::{ pub use self::{
guard::{LocalIrqDisabled, PreemptDisabled}, guard::{LocalIrqDisabled, PreemptDisabled, WriteIrqDisabled},
mutex::{ArcMutexGuard, Mutex, MutexGuard}, mutex::{ArcMutexGuard, Mutex, MutexGuard},
rwlock::{ rwlock::{
ArcRwLockReadGuard, ArcRwLockUpgradeableGuard, ArcRwLockWriteGuard, RwLock, ArcRwLockReadGuard, ArcRwLockUpgradeableGuard, ArcRwLockWriteGuard, RwLock,

View File

@ -230,7 +230,7 @@ impl<T: ?Sized, G: Guardian> RwLock<T, G> {
/// ///
/// This function will never spin-wait and will return immediately. /// This function will never spin-wait and will return immediately.
pub fn try_read(&self) -> Option<RwLockReadGuard<T, G>> { pub fn try_read(&self) -> Option<RwLockReadGuard<T, G>> {
let guard = G::guard(); let guard = G::read_guard();
let lock = self.lock.fetch_add(READER, Acquire); let lock = self.lock.fetch_add(READER, Acquire);
if lock & (WRITER | MAX_READER | BEING_UPGRADED) == 0 { if lock & (WRITER | MAX_READER | BEING_UPGRADED) == 0 {
Some(RwLockReadGuard { inner: self, guard }) Some(RwLockReadGuard { inner: self, guard })
@ -247,7 +247,7 @@ impl<T: ?Sized, G: Guardian> RwLock<T, G> {
/// ///
/// [`try_read`]: Self::try_read /// [`try_read`]: Self::try_read
pub fn try_read_arc(self: &Arc<Self>) -> Option<ArcRwLockReadGuard<T, G>> { pub fn try_read_arc(self: &Arc<Self>) -> Option<ArcRwLockReadGuard<T, G>> {
let guard = G::guard(); let guard = G::read_guard();
let lock = self.lock.fetch_add(READER, Acquire); let lock = self.lock.fetch_add(READER, Acquire);
if lock & (WRITER | MAX_READER | BEING_UPGRADED) == 0 { if lock & (WRITER | MAX_READER | BEING_UPGRADED) == 0 {
Some(ArcRwLockReadGuard { Some(ArcRwLockReadGuard {
@ -375,7 +375,7 @@ unsafe impl<T: ?Sized + Sync, R: Deref<Target = RwLock<T, G>> + Clone + Sync, G:
#[clippy::has_significant_drop] #[clippy::has_significant_drop]
#[must_use] #[must_use]
pub struct RwLockReadGuard_<T: ?Sized, R: Deref<Target = RwLock<T, G>> + Clone, G: Guardian> { pub struct RwLockReadGuard_<T: ?Sized, R: Deref<Target = RwLock<T, G>> + Clone, G: Guardian> {
guard: G::Guard, guard: G::ReadGuard,
inner: R, inner: R,
} }