Skip to content

Commit 4fac6a0

Browse files
committed
Add a spinlock implementation to the sync module.
1 parent aa6fb3f commit 4fac6a0

File tree

4 files changed

+144
-7
lines changed

4 files changed

+144
-7
lines changed

include/linux/spinlock.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -331,12 +331,17 @@ static __always_inline raw_spinlock_t *spinlock_check(spinlock_t *lock)
331331

332332
#ifdef CONFIG_DEBUG_SPINLOCK
333333

334-
# define spin_lock_init(lock) \
335-
do { \
336-
static struct lock_class_key __key; \
337-
\
338-
__raw_spin_lock_init(spinlock_check(lock), \
339-
#lock, &__key, LD_WAIT_CONFIG); \
334+
static inline void __spin_lock_init(spinlock_t *lock, const char *name,
335+
struct lock_class_key *key)
336+
{
337+
__raw_spin_lock_init(spinlock_check(lock), name, key, LD_WAIT_CONFIG);
338+
}
339+
340+
# define spin_lock_init(lock) \
341+
do { \
342+
static struct lock_class_key __key; \
343+
\
344+
__spin_lock_init(lock, #lock, &__key); \
340345
} while (0)
341346

342347
#else

rust/helpers.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,29 @@ unsigned long rust_helper_copy_to_user(void __user *to, const void *from, unsign
2424
return copy_to_user(to, from, n);
2525
}
2626

27+
void rust_helper_spin_lock_init(spinlock_t *lock, const char *name,
28+
struct lock_class_key *key)
29+
{
30+
#ifdef CONFIG_DEBUG_SPINLOCK
31+
__spin_lock_init(lock, name, key);
32+
#else
33+
spin_lock_init(lock);
34+
#endif
35+
}
36+
EXPORT_SYMBOL(rust_helper_spin_lock_init);
37+
38+
void rust_helper_spin_lock(spinlock_t *lock)
39+
{
40+
spin_lock(lock);
41+
}
42+
EXPORT_SYMBOL(rust_helper_spin_lock);
43+
44+
void rust_helper_spin_unlock(spinlock_t *lock)
45+
{
46+
spin_unlock(lock);
47+
}
48+
EXPORT_SYMBOL(rust_helper_spin_unlock);
49+
2750
// See https://github.com/rust-lang/rust-bindgen/issues/1671
2851
static_assert(__builtin_types_compatible_p(size_t, uintptr_t),
2952
"size_t must match uintptr_t, what architecture is this??");

rust/kernel/sync/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
88
mod guard;
99
mod mutex;
10+
mod spinlock;
1011

1112
pub use guard::{Guard, Lock};
1213
pub use mutex::Mutex;
14+
pub use spinlock::SpinLock;
1315

1416
/// Safely initialises an object that has an `init` function that takes a name and a lock class as
15-
/// arguments, for example, [`Mutex`].
17+
/// arguments, examples of these are [`Mutex`] and [`SpinLock`]. Each of them also provides a more
18+
/// specialised name that uses this macro.
1619
#[macro_export]
1720
macro_rules! init_with_lockdep {
1821
($obj:expr, $name:literal) => {{

rust/kernel/sync/spinlock.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! A kernel spinlock.
4+
//!
5+
//! This module allows Rust code to use the kernel's [`struct spinlock`].
6+
7+
use super::{Guard, Lock};
8+
use crate::{bindings, c_types, CStr};
9+
use core::{cell::UnsafeCell, pin::Pin};
10+
11+
extern "C" {
12+
#[allow(improper_ctypes)]
13+
fn rust_helper_spin_lock_init(
14+
lock: *mut bindings::spinlock_t,
15+
name: *const c_types::c_char,
16+
key: *mut bindings::lock_class_key,
17+
);
18+
fn rust_helper_spin_lock(lock: *mut bindings::spinlock);
19+
fn rust_helper_spin_unlock(lock: *mut bindings::spinlock);
20+
}
21+
22+
/// Safely initialises a [`SpinLock`] with the given name, generating a new lock class.
23+
#[macro_export]
24+
macro_rules! spinlock_init {
25+
($spinlock:expr, $name:literal) => {
26+
$crate::init_with_lockdep!($spinlock, $name)
27+
};
28+
}
29+
30+
/// Exposes the kernel's [`spinlock_t`]. When multiple CPUs attempt to lock the same spinlock, only
31+
/// one at a time is allowed to progress, the others will block (spinnig) until the spinlock is
32+
/// unlocked, at which point another CPU will be allowed to make progress.
33+
///
34+
/// A [`SpinLock`] must first be initialised with a call to [`SpinLock::init`] before it can be
35+
/// used. The [`spinlock_init`] macro is provided to automatically assign a new lock class to a
36+
/// spinlock instance.
37+
///
38+
/// [`SpinLock`] does not manage the interrupt state, so it can be used in only two cases: (a) when
39+
/// the caller knows that interrupts are disabled, or (b) when callers never use it in interrupt
40+
/// handlers (in which case it is ok for interrupts to be enabled).
41+
///
42+
/// [`spinlock_t`]: ../../../include/linux/spinlock.h
43+
pub struct SpinLock<T: ?Sized> {
44+
spin_lock: UnsafeCell<bindings::spinlock>,
45+
data: UnsafeCell<T>,
46+
}
47+
48+
// SAFETY: `SpinLock` can be transferred across thread boundaries iff the data it protects can.
49+
unsafe impl<T: ?Sized + Send> Send for SpinLock<T> {}
50+
51+
// SAFETY: `SpinLock` serialises the interior mutability it provides, so it is `Sync` as long as the
52+
// data it protects is also `Sync`.
53+
unsafe impl<T: ?Sized + Sync> Sync for SpinLock<T> {}
54+
55+
impl<T> SpinLock<T> {
56+
/// Constructs a new spinlock.
57+
///
58+
/// # Safety
59+
///
60+
/// The caller must call [`SpinLock::init`] before using the spinlock.
61+
pub unsafe fn new(t: T) -> Self {
62+
Self {
63+
spin_lock: UnsafeCell::new(bindings::spinlock::default()),
64+
data: UnsafeCell::new(t),
65+
}
66+
}
67+
}
68+
69+
impl<T: ?Sized> SpinLock<T> {
70+
/// Initialises the spinlock so that it can be safely used.
71+
///
72+
/// Callers are encouraged to use the [`spinlock_init`] macro as it automatically creates a new
73+
/// lock class on each usage.
74+
///
75+
/// # Safety
76+
///
77+
/// `key` must point to a valid memory location as it will be used by the kernel.
78+
pub unsafe fn init(self: Pin<&Self>, name: CStr<'static>, key: *mut bindings::lock_class_key) {
79+
rust_helper_spin_lock_init(self.spin_lock.get(), name.as_ptr() as _, key);
80+
}
81+
82+
/// Locks the spinlock and gives the caller access to the data protected by it. Only one thread
83+
/// at a time is allowed to access the protected data.
84+
pub fn lock(&self) -> Guard<Self> {
85+
self.lock_noguard();
86+
// SAFETY: The spinlock was just acquired.
87+
unsafe { Guard::new(self) }
88+
}
89+
}
90+
91+
impl<T: ?Sized> Lock for SpinLock<T> {
92+
type Inner = T;
93+
94+
fn lock_noguard(&self) {
95+
// SAFETY: `spin_lock` points to valid memory.
96+
unsafe { rust_helper_spin_lock(self.spin_lock.get()) };
97+
}
98+
99+
unsafe fn unlock(&self) {
100+
rust_helper_spin_unlock(self.spin_lock.get());
101+
}
102+
103+
unsafe fn locked_data(&self) -> &UnsafeCell<T> {
104+
&self.data
105+
}
106+
}

0 commit comments

Comments
 (0)