Skip to content

Commit 1b69584

Browse files
authored
Merge pull request raspberrypi#634 from wedsonaf/rwsem
rust: add abstraction for `rw_semaphore`
2 parents ef320da + 418eaa6 commit 1b69584

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed

rust/kernel/sync/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ mod guard;
2929
mod locked_by;
3030
mod mutex;
3131
mod revocable_mutex;
32+
mod rwsem;
3233
mod seqlock;
3334
mod spinlock;
3435

@@ -38,6 +39,7 @@ pub use guard::{CreatableLock, Guard, Lock, ReadLock, WriteLock};
3839
pub use locked_by::LockedBy;
3940
pub use mutex::Mutex;
4041
pub use revocable_mutex::{RevocableMutex, RevocableMutexGuard};
42+
pub use rwsem::RwSemaphore;
4143
pub use seqlock::{SeqLock, SeqLockReadGuard};
4244
pub use spinlock::SpinLock;
4345

rust/kernel/sync/rwsem.rs

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! A kernel read/write mutex.
4+
//!
5+
//! This module allows Rust code to use the kernel's [`struct rw_semaphore`].
6+
//!
7+
//! C header: [`include/linux/rwsem.h`](../../../../include/linux/rwsem.h)
8+
9+
use super::{mutex::EmptyGuardContext, CreatableLock, Guard, Lock, ReadLock};
10+
use crate::{bindings, str::CStr, Opaque};
11+
use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin};
12+
13+
/// Safely initialises a [`RwSemaphore`] with the given name, generating a new lock class.
14+
#[macro_export]
15+
macro_rules! rwsemaphore_init {
16+
($rwsem:expr, $name:literal) => {
17+
$crate::init_with_lockdep!($rwsem, $name)
18+
};
19+
}
20+
21+
/// Exposes the kernel's [`struct rw_semaphore`].
22+
///
23+
/// It's a read/write mutex. That is, it allows multiple readers to acquire it concurrently, but
24+
/// only one writer at a time. On contention, waiters sleep.
25+
///
26+
/// A [`RwSemaphore`] must first be initialised with a call to [`RwSemaphore::init_lock`] before it
27+
/// can be used. The [`rwsemaphore_init`] macro is provided to automatically assign a new lock
28+
/// class to an [`RwSemaphore`] instance.
29+
///
30+
/// Since it may block, [`RwSemaphore`] needs to be used with care in atomic contexts.
31+
///
32+
/// [`struct rw_semaphore`]: ../../../include/linux/rwsem.h
33+
pub struct RwSemaphore<T: ?Sized> {
34+
/// The kernel `struct rw_semaphore` object.
35+
rwsem: Opaque<bindings::rw_semaphore>,
36+
37+
/// An rwsem needs to be pinned because it contains a [`struct list_head`] that is
38+
/// self-referential, so it cannot be safely moved once it is initialised.
39+
_pin: PhantomPinned,
40+
41+
/// The data protected by the rwsem.
42+
data: UnsafeCell<T>,
43+
}
44+
45+
// SAFETY: `RwSemaphore` can be transferred across thread boundaries iff the data it protects can.
46+
#[allow(clippy::non_send_fields_in_send_ty)]
47+
unsafe impl<T: ?Sized + Send> Send for RwSemaphore<T> {}
48+
49+
// SAFETY: `RwSemaphore` requires that the protected type be `Sync` for it to be `Sync` as well
50+
// because the read mode allows multiple threads to access the protected data concurrently. It
51+
// requires `Send` because the write lock allows a `&mut T` to be accessible from an arbitrary
52+
// thread.
53+
unsafe impl<T: ?Sized + Send + Sync> Sync for RwSemaphore<T> {}
54+
55+
impl<T> RwSemaphore<T> {
56+
/// Constructs a new rw semaphore.
57+
///
58+
/// # Safety
59+
///
60+
/// The caller must call [`RwSemaphore::init_lock`] before using the rw semaphore.
61+
pub unsafe fn new(t: T) -> Self {
62+
Self {
63+
rwsem: Opaque::uninit(),
64+
data: UnsafeCell::new(t),
65+
_pin: PhantomPinned,
66+
}
67+
}
68+
}
69+
70+
impl<T: ?Sized> RwSemaphore<T> {
71+
/// Locks the rw semaphore in write (exclusive) mode and gives the caller access to the data
72+
/// protected by it. Only one thread at a time is allowed to access the protected data.
73+
pub fn write(&self) -> Guard<'_, Self> {
74+
let ctx = <Self as Lock>::lock_noguard(self);
75+
// SAFETY: The rw semaphore was just acquired in write mode.
76+
unsafe { Guard::new(self, ctx) }
77+
}
78+
79+
/// Locks the rw semaphore in read (shared) mode and gives the caller access to the data
80+
/// protected by it. Only one thread at a time is allowed to access the protected data.
81+
pub fn read(&self) -> Guard<'_, Self, ReadLock> {
82+
let ctx = <Self as Lock<ReadLock>>::lock_noguard(self);
83+
// SAFETY: The rw semaphore was just acquired in read mode.
84+
unsafe { Guard::new(self, ctx) }
85+
}
86+
}
87+
88+
impl<T> CreatableLock for RwSemaphore<T> {
89+
unsafe fn new_lock(data: Self::Inner) -> Self {
90+
// SAFETY: The safety requirements of `new_lock` also require that `init_lock` be called.
91+
unsafe { Self::new(data) }
92+
}
93+
94+
unsafe fn init_lock(
95+
self: Pin<&mut Self>,
96+
name: &'static CStr,
97+
key: *mut bindings::lock_class_key,
98+
) {
99+
unsafe { bindings::__init_rwsem(self.rwsem.get(), name.as_char_ptr(), key) };
100+
}
101+
}
102+
103+
// SAFETY: The underlying kernel `struct rw_semaphore` object ensures mutual exclusion because it's
104+
// acquired in write mode.
105+
unsafe impl<T: ?Sized> Lock for RwSemaphore<T> {
106+
type Inner = T;
107+
type GuardContext = EmptyGuardContext;
108+
109+
fn lock_noguard(&self) -> EmptyGuardContext {
110+
// SAFETY: `rwsem` points to valid memory.
111+
unsafe { bindings::down_write(self.rwsem.get()) };
112+
EmptyGuardContext
113+
}
114+
115+
unsafe fn unlock(&self, _: &mut EmptyGuardContext) {
116+
// SAFETY: The safety requirements of the function ensure that the rw semaphore is owned by
117+
// the caller.
118+
unsafe { bindings::up_write(self.rwsem.get()) };
119+
}
120+
121+
fn locked_data(&self) -> &UnsafeCell<T> {
122+
&self.data
123+
}
124+
}
125+
126+
// SAFETY: The underlying kernel `struct rw_semaphore` object ensures that only shared references
127+
// are accessible from other threads because it's acquired in read mode.
128+
unsafe impl<T: ?Sized> Lock<ReadLock> for RwSemaphore<T> {
129+
type Inner = T;
130+
type GuardContext = EmptyGuardContext;
131+
132+
fn lock_noguard(&self) -> EmptyGuardContext {
133+
// SAFETY: `rwsem` points to valid memory.
134+
unsafe { bindings::down_read(self.rwsem.get()) };
135+
EmptyGuardContext
136+
}
137+
138+
unsafe fn unlock(&self, _: &mut EmptyGuardContext) {
139+
// SAFETY: The safety requirements of the function ensure that the rw semaphore is owned by
140+
// the caller.
141+
unsafe { bindings::up_read(self.rwsem.get()) };
142+
}
143+
144+
fn locked_data(&self) -> &UnsafeCell<T> {
145+
&self.data
146+
}
147+
}

0 commit comments

Comments
 (0)