Skip to content

Commit 715a111

Browse files
committed
Add a spinlock implementation to the sync module.
1 parent 8a913df commit 715a111

File tree

5 files changed

+160
-9
lines changed

5 files changed

+160
-9
lines changed

drivers/char/rust_example.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@
99
use alloc::boxed::Box;
1010
use core::pin::Pin;
1111
use kernel::prelude::*;
12-
use kernel::{chrdev, cstr, file_operations::FileOperations, miscdev, mutex_init, sync::Mutex};
12+
use kernel::{
13+
chrdev, cstr,
14+
file_operations::FileOperations,
15+
miscdev, mutex_init, spinlock_init,
16+
sync::{Mutex, SpinLock},
17+
};
1318

1419
module! {
1520
type: RustExample,
@@ -78,7 +83,16 @@ impl KernelModule for RustExample {
7883
{
7984
// SAFETY: `init` is called below.
8085
let data = Pin::from(Box::try_new(unsafe { Mutex::new(0) })?);
81-
mutex_init!(data.as_ref(), "RustExample::init::data");
86+
mutex_init!(data.as_ref(), "RustExample::init::data1");
87+
*data.lock() = 10;
88+
println!("Value: {}", *data.lock());
89+
}
90+
91+
// Test spinlocks.
92+
{
93+
// SAFETY: `init` is called below.
94+
let data = Pin::from(Box::try_new(unsafe { SpinLock::new(0) })?);
95+
spinlock_init!(data.as_ref(), "RustExample::init::data2");
8296
*data.lock() = 10;
8397
println!("Value: {}", *data.lock());
8498
}

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
@@ -22,12 +22,15 @@ use core::pin::Pin;
2222

2323
mod guard;
2424
mod mutex;
25+
mod spinlock;
2526

2627
pub use guard::{Guard, Lock};
2728
pub use mutex::Mutex;
29+
pub use spinlock::SpinLock;
2830

2931
/// Safely initialises an object that has an `init` function that takes a name and a lock class as
30-
/// arguments, for example, [`Mutex`].
32+
/// arguments, examples of these are [`Mutex`] and [`SpinLock`]. Each of them also provides a more
33+
/// specialised name that uses this macro.
3134
#[doc(hidden)]
3235
#[macro_export]
3336
macro_rules! init_with_lockdep {

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, NeedsLockClass};
8+
use crate::{bindings, c_types, CStr};
9+
use core::{cell::UnsafeCell, marker::PhantomPinned, 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+
46+
/// Spinlocks are architecture-defined. So we conservatively require them to be pinned in case
47+
/// some architecture uses self=references now or in the future.
48+
_pin: PhantomPinned,
49+
50+
data: UnsafeCell<T>,
51+
}
52+
53+
// SAFETY: `SpinLock` can be transferred across thread boundaries iff the data it protects can.
54+
unsafe impl<T: ?Sized + Send> Send for SpinLock<T> {}
55+
56+
// SAFETY: `SpinLock` serialises the interior mutability it provides, so it is `Sync` as long as the
57+
// data it protects is `Send`.
58+
unsafe impl<T: ?Sized + Send> Sync for SpinLock<T> {}
59+
60+
impl<T> SpinLock<T> {
61+
/// Constructs a new spinlock.
62+
///
63+
/// # Safety
64+
///
65+
/// The caller must call [`SpinLock::init`] before using the spinlock.
66+
pub unsafe fn new(t: T) -> Self {
67+
Self {
68+
spin_lock: UnsafeCell::new(bindings::spinlock::default()),
69+
data: UnsafeCell::new(t),
70+
_pin: PhantomPinned,
71+
}
72+
}
73+
}
74+
75+
impl<T: ?Sized> SpinLock<T> {
76+
/// Locks the spinlock and gives the caller access to the data protected by it. Only one thread
77+
/// at a time is allowed to access the protected data.
78+
pub fn lock(&self) -> Guard<Self> {
79+
self.lock_noguard();
80+
// SAFETY: The spinlock was just acquired.
81+
unsafe { Guard::new(self) }
82+
}
83+
}
84+
85+
impl<T: ?Sized> NeedsLockClass for SpinLock<T> {
86+
unsafe fn init(self: Pin<&Self>, name: CStr<'static>, key: *mut bindings::lock_class_key) {
87+
rust_helper_spin_lock_init(self.spin_lock.get(), name.as_ptr() as _, key);
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)