Skip to content

Commit 7106a05

Browse files
committed
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`. Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
1 parent a86cf53 commit 7106a05

File tree

2 files changed

+40
-2
lines changed

2 files changed

+40
-2
lines changed

Diff for: rust/kernel/sync/lock.rs

+25
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ pub(crate) mod spinlock;
2222
///
2323
/// - Implementers must ensure that only one thread/CPU may access the protected data once the lock
2424
/// is owned, that is, between calls to `lock` and `unlock`.
25+
/// - Implementers must also ensure that `relock` uses the same locking method as the original
26+
/// lock operation. For example, it should disable interrupts if [`IrqSaveBackend::lock_irqsave`]
27+
/// is used.
2528
pub unsafe trait Backend {
2629
/// The state required by the lock.
2730
type State;
@@ -55,6 +58,17 @@ pub unsafe trait Backend {
5558
///
5659
/// It must only be called by the current owner of the lock.
5760
unsafe fn unlock(ptr: *mut Self::State, guard_state: &Self::GuardState);
61+
62+
/// Reacquires the lock, making the caller its owner.
63+
///
64+
/// # Safety
65+
///
66+
/// Callers must ensure that `state` comes from a previous call to [`Backend::lock`] (or
67+
/// variant) that has been unlocked with [`Backend::unlock`] and will be relocked now.
68+
unsafe fn relock(ptr: *mut Self::State, guard_state: &mut Self::GuardState) {
69+
// SAFETY: The safety requirements ensure that the lock is initialised.
70+
*guard_state = unsafe { Self::lock(ptr) };
71+
}
5872
}
5973

6074
/// The "backend" of a lock that supports the irq-save variant.
@@ -162,6 +176,17 @@ pub struct Guard<'a, T: ?Sized, B: Backend> {
162176
// SAFETY: `Guard` is sync when the data protected by the lock is also sync.
163177
unsafe impl<T: Sync + ?Sized, B: Backend> Sync for Guard<'_, T, B> {}
164178

179+
impl<T: ?Sized, B: Backend> Guard<'_, T, B> {
180+
#[allow(dead_code)]
181+
pub(crate) fn do_unlocked(&mut self, cb: impl FnOnce()) {
182+
// SAFETY: The caller owns the lock, so it is safe to unlock it.
183+
unsafe { B::unlock(self.lock.state.get(), &self.state) };
184+
cb();
185+
// SAFETY: The lock was just unlocked above and is being relocked now.
186+
unsafe { B::relock(self.lock.state.get(), &mut self.state) };
187+
}
188+
}
189+
165190
impl<T: ?Sized, B: Backend> core::ops::Deref for Guard<'_, T, B> {
166191
type Target = T;
167192

Diff for: rust/kernel/sync/lock/spinlock.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//!
55
//! This module allows Rust code to use the kernel's `spinlock_t`.
66
7+
use super::IrqSaveBackend;
78
use crate::bindings;
89

910
/// Creates a [`SpinLock`] initialiser with the given name and a newly-created lock class.
@@ -92,7 +93,8 @@ pub type SpinLock<T> = super::Lock<T, SpinLockBackend>;
9293
/// A kernel `spinlock_t` lock backend.
9394
pub struct SpinLockBackend;
9495

95-
// SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion.
96+
// SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion. `relock` uses the
97+
// same scheme as `unlock` to figure out which locking method was used originally.
9698
unsafe impl super::Backend for SpinLockBackend {
9799
type State = bindings::spinlock_t;
98100
type GuardState = Option<core::ffi::c_ulong>;
@@ -124,13 +126,24 @@ unsafe impl super::Backend for SpinLockBackend {
124126
None => unsafe { bindings::spin_unlock(ptr) },
125127
}
126128
}
129+
130+
unsafe fn relock(ptr: *mut Self::State, guard_state: &mut Self::GuardState) {
131+
let _ = match guard_state {
132+
// SAFETY: The safety requiments of this function ensure that `ptr` has been
133+
// initialised.
134+
None => unsafe { Self::lock(ptr) },
135+
// SAFETY: The safety requiments of this function ensure that `ptr` has been
136+
// initialised.
137+
Some(_) => unsafe { Self::lock_irqsave(ptr) },
138+
};
139+
}
127140
}
128141

129142
// SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion. We use the `irqsave`
130143
// variant of the C lock acquisition functions to disable interrupts and retrieve the original
131144
// interrupt state, and the `irqrestore` variant of the lock release functions to restore the state
132145
// in `unlock` -- we use the guard context to determine which method was used to acquire the lock.
133-
unsafe impl super::IrqSaveBackend for SpinLockBackend {
146+
unsafe impl IrqSaveBackend for SpinLockBackend {
134147
unsafe fn lock_irqsave(ptr: *mut Self::State) -> Self::GuardState {
135148
// SAFETY: The safety requirements of this function ensure that `ptr` points to valid
136149
// memory, and that it has been initialised before.

0 commit comments

Comments
 (0)