mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-24 14:07:52 -04:00
rust: lock: add Guard::do_unlocked
It releases the lock, executes some function provided by the caller, then reacquires the lock. This is preparation for the implementation of condvars, which will sleep after between unlocking and relocking. We need an explicit `relock` method for primitives like `SpinLock` that have an irqsave variant: we use the guard state to determine if the lock was originally acquired with the regular `lock` function or `lock_irqsave`. Reviewed-by: Martin Rodriguez Reboredo <yakoyoku@gmail.com> Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com> Link: https://lore.kernel.org/rust-for-linux/20230412121431.41627-1-wedsonaf@gmail.com/ [ Removed the irqsave bits as discussed. ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
This commit is contained in:
parent
7b1f55e3a9
commit
e32cca32c3
2 changed files with 30 additions and 2 deletions
|
@ -6,7 +6,7 @@
|
||||||
//! spinlocks, raw spinlocks) to be provided with minimal effort.
|
//! spinlocks, raw spinlocks) to be provided with minimal effort.
|
||||||
|
|
||||||
use super::LockClassKey;
|
use super::LockClassKey;
|
||||||
use crate::{bindings, init::PinInit, pin_init, str::CStr, types::Opaque};
|
use crate::{bindings, init::PinInit, pin_init, str::CStr, types::Opaque, types::ScopeGuard};
|
||||||
use core::{cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned};
|
use core::{cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned};
|
||||||
use macros::pin_data;
|
use macros::pin_data;
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@ pub mod spinlock;
|
||||||
///
|
///
|
||||||
/// - Implementers must ensure that only one thread/CPU may access the protected data once the lock
|
/// - Implementers must ensure that only one thread/CPU may access the protected data once the lock
|
||||||
/// is owned, that is, between calls to `lock` and `unlock`.
|
/// is owned, that is, between calls to `lock` and `unlock`.
|
||||||
|
/// - Implementers must also ensure that `relock` uses the same locking method as the original
|
||||||
|
/// lock operation.
|
||||||
pub unsafe trait Backend {
|
pub unsafe trait Backend {
|
||||||
/// The state required by the lock.
|
/// The state required by the lock.
|
||||||
type State;
|
type State;
|
||||||
|
@ -55,6 +57,17 @@ pub unsafe trait Backend {
|
||||||
///
|
///
|
||||||
/// It must only be called by the current owner of the lock.
|
/// It must only be called by the current owner of the lock.
|
||||||
unsafe fn unlock(ptr: *mut Self::State, guard_state: &Self::GuardState);
|
unsafe fn unlock(ptr: *mut Self::State, guard_state: &Self::GuardState);
|
||||||
|
|
||||||
|
/// Reacquires the lock, making the caller its owner.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Callers must ensure that `guard_state` comes from a previous call to [`Backend::lock`] (or
|
||||||
|
/// variant) that has been unlocked with [`Backend::unlock`] and will be relocked now.
|
||||||
|
unsafe fn relock(ptr: *mut Self::State, guard_state: &mut Self::GuardState) {
|
||||||
|
// SAFETY: The safety requirements ensure that the lock is initialised.
|
||||||
|
*guard_state = unsafe { Self::lock(ptr) };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mutual exclusion primitive.
|
/// A mutual exclusion primitive.
|
||||||
|
@ -126,6 +139,20 @@ pub struct Guard<'a, T: ?Sized, B: Backend> {
|
||||||
// SAFETY: `Guard` is sync when the data protected by the lock is also sync.
|
// SAFETY: `Guard` is sync when the data protected by the lock is also sync.
|
||||||
unsafe impl<T: Sync + ?Sized, B: Backend> Sync for Guard<'_, T, B> {}
|
unsafe impl<T: Sync + ?Sized, B: Backend> Sync for Guard<'_, T, B> {}
|
||||||
|
|
||||||
|
impl<T: ?Sized, B: Backend> Guard<'_, T, B> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn do_unlocked(&mut self, cb: impl FnOnce()) {
|
||||||
|
// SAFETY: The caller owns the lock, so it is safe to unlock it.
|
||||||
|
unsafe { B::unlock(self.lock.state.get(), &self.state) };
|
||||||
|
|
||||||
|
// SAFETY: The lock was just unlocked above and is being relocked now.
|
||||||
|
let _relock =
|
||||||
|
ScopeGuard::new(|| unsafe { B::relock(self.lock.state.get(), &mut self.state) });
|
||||||
|
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: ?Sized, B: Backend> core::ops::Deref for Guard<'_, T, B> {
|
impl<T: ?Sized, B: Backend> core::ops::Deref for Guard<'_, T, B> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,8 @@ pub type SpinLock<T> = super::Lock<T, SpinLockBackend>;
|
||||||
/// A kernel `spinlock_t` lock backend.
|
/// A kernel `spinlock_t` lock backend.
|
||||||
pub struct SpinLockBackend;
|
pub struct SpinLockBackend;
|
||||||
|
|
||||||
// SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion.
|
// SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion. `relock` uses the
|
||||||
|
// default implementation that always calls the same locking method.
|
||||||
unsafe impl super::Backend for SpinLockBackend {
|
unsafe impl super::Backend for SpinLockBackend {
|
||||||
type State = bindings::spinlock_t;
|
type State = bindings::spinlock_t;
|
||||||
type GuardState = ();
|
type GuardState = ();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue