Skip to content

Commit fc3740d

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 <[email protected]>
1 parent 7b3fef7 commit fc3740d

File tree

2 files changed

+35
-1
lines changed

2 files changed

+35
-1
lines changed

rust/kernel/sync/lock.rs

+22
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ pub unsafe trait Backend {
5555
///
5656
/// It must only be called by the current owner of the lock.
5757
unsafe fn unlock(ptr: *mut Self::State, state: &Self::GuardState);
58+
59+
/// Reacquires the lock, making the caller its owner.
60+
///
61+
/// # Safety
62+
///
63+
/// Callers must ensure that `state` comes from a previous call to [`Backend::lock`] that has
64+
/// been unlocked with [`Backend::unlock`] and will be relocked now.
65+
unsafe fn relock(ptr: *mut Self::State, state: &mut Self::GuardState) {
66+
// SAFETY: The safety requirements ensure that the lock is initialised.
67+
*state = unsafe { Self::lock(ptr) };
68+
}
5869
}
5970

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

172+
impl<T: ?Sized, B: Backend> Guard<'_, T, B> {
173+
#[allow(dead_code)]
174+
pub(crate) fn do_unlocked(&mut self, cb: impl FnOnce()) {
175+
// SAFETY: The caller owns the lock, so it is safe to unlock it.
176+
unsafe { B::unlock(self.lock.state.get(), &self.state) };
177+
cb();
178+
// SAFETY: The lock was just unlocked above and is being relocked now.
179+
unsafe { B::relock(self.lock.state.get(), &mut self.state) };
180+
}
181+
}
182+
161183
impl<T: ?Sized, B: Backend> core::ops::Deref for Guard<'_, T, B> {
162184
type Target = T;
163185

rust/kernel/sync/lock/spinlock.rs

+13-1
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.
@@ -121,10 +122,21 @@ unsafe impl super::Backend for SpinLockBackend {
121122
None => unsafe { bindings::spin_unlock(ptr) },
122123
}
123124
}
125+
126+
unsafe fn relock(ptr: *mut Self::State, state: &mut Self::GuardState) {
127+
let _ = match state {
128+
// SAFETY: The safety requiments of this function ensure that `ptr` has been
129+
// initialised.
130+
None => unsafe { Self::lock(ptr) },
131+
// SAFETY: The safety requiments of this function ensure that `ptr` has been
132+
// initialised.
133+
Some(_) => unsafe { Self::lock_irqsave(ptr) },
134+
};
135+
}
124136
}
125137

126138
// SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion.
127-
unsafe impl super::IrqSaveBackend for SpinLockBackend {
139+
unsafe impl IrqSaveBackend for SpinLockBackend {
128140
unsafe fn lock_irqsave(ptr: *mut Self::State) -> Self::GuardState {
129141
// SAFETY: The safety requirements of this function ensure that `ptr` points to valid
130142
// memory, and that it has been initialised before.

0 commit comments

Comments
 (0)