Skip to content

Commit 51a196e

Browse files
authored
Merge pull request #103 from Rust-for-Linux/spinlock
Spinlock
2 parents d320a36 + ba93231 commit 51a196e

File tree

5 files changed

+162
-9
lines changed

5 files changed

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

0 commit comments

Comments
 (0)