Skip to content

Commit 7407571

Browse files
authored
Merge pull request #539 from wedsonaf/spinlock
rust: Add support for irqsave/irqrestore variant of spin lock/unlock.
2 parents 7d1f09f + 6d6e1d5 commit 7407571

File tree

6 files changed

+116
-15
lines changed

6 files changed

+116
-15
lines changed

rust/helpers.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,20 @@ void rust_helper_spin_unlock(spinlock_t *lock)
114114
}
115115
EXPORT_SYMBOL_GPL(rust_helper_spin_unlock);
116116

117+
unsigned long rust_helper_spin_lock_irqsave(spinlock_t *lock)
118+
{
119+
unsigned long flags;
120+
spin_lock_irqsave(lock, flags);
121+
return flags;
122+
}
123+
EXPORT_SYMBOL_GPL(rust_helper_spin_lock_irqsave);
124+
125+
void rust_helper_spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
126+
{
127+
spin_unlock_irqrestore(lock, flags);
128+
}
129+
EXPORT_SYMBOL_GPL(rust_helper_spin_unlock_irqrestore);
130+
117131
void rust_helper_init_wait(struct wait_queue_entry *wq_entry)
118132
{
119133
init_wait(wq_entry);

rust/kernel/sync/condvar.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ impl CondVar {
8282
// SAFETY: No arguments, switches to another thread.
8383
unsafe { bindings::schedule() };
8484

85-
guard.guard.context = lock.lock_noguard();
85+
lock.relock(&mut guard.guard.context);
8686

8787
// SAFETY: Both `wait` and `wait_list` point to valid memory.
8888
unsafe { bindings::finish_wait(self.wait_list.get(), wait.get()) };

rust/kernel/sync/guard.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,19 @@ pub unsafe trait Lock {
122122
type GuardContext;
123123

124124
/// Acquires the lock, making the caller its owner.
125+
#[must_use]
125126
fn lock_noguard(&self) -> Self::GuardContext;
126127

128+
/// Reacquires the lock, making the caller its owner.
129+
///
130+
/// The guard context before the last unlock is passed in.
131+
///
132+
/// Locks that don't require this state on relock can simply use the default implementation
133+
/// that calls [`Lock::lock_noguard`].
134+
fn relock(&self, ctx: &mut Self::GuardContext) {
135+
*ctx = self.lock_noguard();
136+
}
137+
127138
/// Releases the lock, giving up ownership of the lock.
128139
///
129140
/// # Safety

rust/kernel/sync/mutex.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@ impl<T: ?Sized> Mutex<T> {
6565
/// Locks the mutex and gives the caller access to the data protected by it. Only one thread at
6666
/// a time is allowed to access the protected data.
6767
pub fn lock(&self) -> GuardMut<'_, Self> {
68-
self.lock_noguard();
68+
let ctx = self.lock_noguard();
6969
// SAFETY: The mutex was just acquired.
70-
unsafe { GuardMut::new(self, ()) }
70+
unsafe { GuardMut::new(self, ctx) }
7171
}
7272
}
7373

rust/kernel/sync/seqlock.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,12 @@ unsafe impl<L: CreatableLock + ?Sized> Lock for SeqLock<L> {
156156
ctx
157157
}
158158

159+
fn relock(&self, ctx: &mut L::GuardContext) {
160+
self.write_lock.relock(ctx);
161+
// SAFETY: `count` contains valid memory.
162+
unsafe { bindings::write_seqcount_begin(self.count.get()) };
163+
}
164+
159165
unsafe fn unlock(&self, ctx: &mut L::GuardContext) {
160166
// SAFETY: The safety requirements of the function ensure that lock is owned by the caller.
161167
unsafe { bindings::write_seqcount_end(self.count.get()) };

rust/kernel/sync/spinlock.rs

Lines changed: 82 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//! See <https://www.kernel.org/doc/Documentation/locking/spinlocks.txt>.
88
99
use super::{CreatableLock, GuardMut, Lock};
10-
use crate::{bindings, str::CStr, Opaque};
10+
use crate::{bindings, c_types, str::CStr, Opaque};
1111
use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin};
1212

1313
/// Safely initialises a [`SpinLock`] with the given name, generating a new lock class.
@@ -26,9 +26,51 @@ macro_rules! spinlock_init {
2626
/// used. The [`spinlock_init`] macro is provided to automatically assign a new lock class to a
2727
/// spinlock instance.
2828
///
29-
/// [`SpinLock`] does not manage the interrupt state, so it can be used in only two cases: (a) when
30-
/// the caller knows that interrupts are disabled, or (b) when callers never use it in interrupt
31-
/// handlers (in which case it is ok for interrupts to be enabled).
29+
/// There are two ways to acquire the lock:
30+
/// - [`SpinLock::lock`], which doesn't manage interrupt state, so it should be used in only two
31+
/// cases: (a) when the caller knows that interrupts are disabled, or (b) when callers never use
32+
/// it in atomic context (e.g., interrupt handlers), in which case it is ok for interrupts to be
33+
/// enabled.
34+
/// - [`SpinLock::lock_irqdisable`], which disables interrupts if they are enabled before
35+
/// acquiring the lock. When the lock is released, the interrupt state is automatically returned
36+
/// to its value before [`SpinLock::lock_irqdisable`] was called.
37+
///
38+
/// # Examples
39+
///
40+
/// ```
41+
/// # use kernel::prelude::*;
42+
/// # use kernel::sync::SpinLock;
43+
/// # use core::pin::Pin;
44+
///
45+
/// struct Example {
46+
/// a: u32,
47+
/// b: u32,
48+
/// }
49+
///
50+
/// // Function that acquires spinlock without changing interrupt state.
51+
/// fn lock_example(value: &SpinLock<Example>) {
52+
/// let mut guard = value.lock();
53+
/// guard.a = 10;
54+
/// guard.b = 20;
55+
/// }
56+
///
57+
/// // Function that acquires spinlock and disables interrupts while holding it.
58+
/// fn lock_irqdisable_example(value: &SpinLock<Example>) {
59+
/// let mut guard = value.lock_irqdisable();
60+
/// guard.a = 30;
61+
/// guard.b = 40;
62+
/// }
63+
///
64+
/// // Initialises a spinlock and calls the example functions.
65+
/// pub fn spinlock_example() {
66+
/// // SAFETY: `spinlock_init` is called below.
67+
/// let mut value = unsafe { SpinLock::new(Example { a: 1, b: 2 }) };
68+
/// // SAFETY: We don't move `value`.
69+
/// kernel::spinlock_init!(unsafe { Pin::new_unchecked(&mut value) }, "value");
70+
/// lock_example(&value);
71+
/// lock_irqdisable_example(&value);
72+
/// }
73+
/// ```
3274
///
3375
/// [`spinlock_t`]: ../../../include/linux/spinlock.h
3476
pub struct SpinLock<T: ?Sized> {
@@ -67,9 +109,24 @@ impl<T: ?Sized> SpinLock<T> {
67109
/// Locks the spinlock and gives the caller access to the data protected by it. Only one thread
68110
/// at a time is allowed to access the protected data.
69111
pub fn lock(&self) -> GuardMut<'_, Self> {
70-
self.lock_noguard();
112+
let ctx = self.lock_noguard();
71113
// SAFETY: The spinlock was just acquired.
72-
unsafe { GuardMut::new(self, ()) }
114+
unsafe { GuardMut::new(self, ctx) }
115+
}
116+
117+
/// Locks the spinlock and gives the caller access to the data protected by it. Additionally it
118+
/// disables interrupts (if they are enabled).
119+
///
120+
/// When the lock in unlocked, the interrupt state (enabled/disabled) is restored.
121+
pub fn lock_irqdisable(&self) -> GuardMut<'_, Self> {
122+
let ctx = self.internal_lock_irqsave();
123+
// SAFETY: The spinlock was just acquired.
124+
unsafe { GuardMut::new(self, Some(ctx)) }
125+
}
126+
127+
fn internal_lock_irqsave(&self) -> c_types::c_ulong {
128+
// SAFETY: `spin_lock` points to valid memory.
129+
unsafe { bindings::spin_lock_irqsave(self.spin_lock.get()) }
73130
}
74131
}
75132

@@ -91,17 +148,30 @@ impl<T> CreatableLock for SpinLock<T> {
91148
// SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion.
92149
unsafe impl<T: ?Sized> Lock for SpinLock<T> {
93150
type Inner = T;
94-
type GuardContext = ();
151+
type GuardContext = Option<c_types::c_ulong>;
95152

96-
fn lock_noguard(&self) {
153+
fn lock_noguard(&self) -> Option<c_types::c_ulong> {
97154
// SAFETY: `spin_lock` points to valid memory.
98155
unsafe { bindings::spin_lock(self.spin_lock.get()) };
156+
None
99157
}
100158

101-
unsafe fn unlock(&self, _: &mut ()) {
102-
// SAFETY: The safety requirements of the function ensure that the spinlock is owned by the
103-
// caller.
104-
unsafe { bindings::spin_unlock(self.spin_lock.get()) };
159+
unsafe fn unlock(&self, ctx: &mut Option<c_types::c_ulong>) {
160+
match ctx {
161+
// SAFETY: The safety requirements of the function ensure that the spinlock is owned by
162+
// the caller.
163+
Some(v) => unsafe { bindings::spin_unlock_irqrestore(self.spin_lock.get(), *v) },
164+
// SAFETY: The safety requirements of the function ensure that the spinlock is owned by
165+
// the caller.
166+
None => unsafe { bindings::spin_unlock(self.spin_lock.get()) },
167+
}
168+
}
169+
170+
fn relock(&self, ctx: &mut Self::GuardContext) {
171+
match ctx {
172+
Some(v) => *v = self.internal_lock_irqsave(),
173+
None => *ctx = self.lock_noguard(),
174+
}
105175
}
106176

107177
fn locked_data(&self) -> &UnsafeCell<T> {

0 commit comments

Comments
 (0)