From: Wedson Almeida Filho <wedsonaf@gmail.com>
To: rust-for-linux@vger.kernel.org
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
linux-kernel@vger.kernel.org,
"Wedson Almeida Filho" <walmeida@microsoft.com>,
"Martin Rodriguez Reboredo" <yakoyoku@gmail.com>
Subject: [PATCH v4 02/13] rust: sync: introduce `Lock` and `Guard`
Date: Tue, 11 Apr 2023 02:45:32 -0300 [thread overview]
Message-ID: <20230411054543.21278-2-wedsonaf@gmail.com> (raw)
In-Reply-To: <20230411054543.21278-1-wedsonaf@gmail.com>
From: Wedson Almeida Filho <walmeida@microsoft.com>
They are generic Rust implementations of a lock and a lock guard that
contain code that is common to all locks. Different backends will be
introduced in subsequent commits.
Reviewed-by: Martin Rodriguez Reboredo <yakoyoku@gmail.com>
Suggested-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
---
v1 -> v2: No changes
v2 -> v3: Use new Opaque::ffi_init from Benno's series
v3 -> v4: Fixed name of parameter in Lock comment
rust/kernel/sync.rs | 2 +-
rust/kernel/sync/lock.rs | 162 +++++++++++++++++++++++++++++++++++++++
2 files changed, 163 insertions(+), 1 deletion(-)
create mode 100644 rust/kernel/sync/lock.rs
diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
index 541d235ffbeb..81b0998eaa18 100644
--- a/rust/kernel/sync.rs
+++ b/rust/kernel/sync.rs
@@ -8,6 +8,7 @@
use crate::types::Opaque;
mod arc;
+pub mod lock;
pub use arc::{Arc, ArcBorrow, UniqueArc};
@@ -25,7 +26,6 @@ impl LockClassKey {
Self(Opaque::uninit())
}
- #[allow(dead_code)]
pub(crate) fn as_ptr(&self) -> *mut bindings::lock_class_key {
self.0.get()
}
diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
new file mode 100644
index 000000000000..1a8ecccf4f24
--- /dev/null
+++ b/rust/kernel/sync/lock.rs
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Generic kernel lock and guard.
+//!
+//! It contains a generic Rust lock and guard that allow for different backends (e.g., mutexes,
+//! spinlocks, raw spinlocks) to be provided with minimal effort.
+
+use super::LockClassKey;
+use crate::{bindings, init::PinInit, pin_init, str::CStr, types::Opaque};
+use core::{cell::UnsafeCell, marker::PhantomData, marker::PhantomPinned};
+use macros::pin_data;
+
+/// The "backend" of a lock.
+///
+/// It is the actual implementation of the lock, without the need to repeat patterns used in all
+/// locks.
+///
+/// # Safety
+///
+/// - Implementers must ensure that only one thread/CPU may access the protected data once the lock
+/// is owned, that is, between calls to `lock` and `unlock`.
+pub unsafe trait Backend {
+ /// The state required by the lock.
+ type State;
+
+ /// The state required to be kept between lock and unlock.
+ type GuardState;
+
+ /// Initialises the lock.
+ ///
+ /// # Safety
+ ///
+ /// `ptr` must be valid for write for the duration of the call, while `name` and `key` must
+ /// remain valid for read indefinitely.
+ unsafe fn init(
+ ptr: *mut Self::State,
+ name: *const core::ffi::c_char,
+ key: *mut bindings::lock_class_key,
+ );
+
+ /// Acquires the lock, making the caller its owner.
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that [`Backend::init`] has been previously called.
+ #[must_use]
+ unsafe fn lock(ptr: *mut Self::State) -> Self::GuardState;
+
+ /// Releases the lock, giving up its ownership.
+ ///
+ /// # Safety
+ ///
+ /// It must only be called by the current owner of the lock.
+ unsafe fn unlock(ptr: *mut Self::State, guard_state: &Self::GuardState);
+}
+
+/// A mutual exclusion primitive.
+///
+/// Exposes one of the kernel locking primitives. Which one is exposed depends on the lock banckend
+/// specified as the generic parameter `B`.
+#[pin_data]
+pub struct Lock<T: ?Sized, B: Backend> {
+ /// The kernel lock object.
+ #[pin]
+ state: Opaque<B::State>,
+
+ /// Some locks are known to be self-referential (e.g., mutexes), while others are architecture
+ /// or config defined (e.g., spinlocks). So we conservatively require them to be pinned in case
+ /// some architecture uses self-references now or in the future.
+ #[pin]
+ _pin: PhantomPinned,
+
+ /// The data protected by the lock.
+ data: UnsafeCell<T>,
+}
+
+// SAFETY: `Lock` can be transferred across thread boundaries iff the data it protects can.
+unsafe impl<T: ?Sized + Send, B: Backend> Send for Lock<T, B> {}
+
+// SAFETY: `Lock` serialises the interior mutability it provides, so it is `Sync` as long as the
+// data it protects is `Send`.
+unsafe impl<T: ?Sized + Send, B: Backend> Sync for Lock<T, B> {}
+
+impl<T, B: Backend> Lock<T, B> {
+ /// Constructs a new lock initialiser.
+ #[allow(clippy::new_ret_no_self)]
+ pub fn new(t: T, name: &'static CStr, key: &'static LockClassKey) -> impl PinInit<Self> {
+ pin_init!(Self {
+ data: UnsafeCell::new(t),
+ _pin: PhantomPinned,
+ // SAFETY: `slot` is valid while the closure is called and both `name` and `key` have
+ // static lifetimes so they live indefinitely.
+ state <- Opaque::ffi_init(|slot| unsafe {
+ B::init(slot, name.as_char_ptr(), key.as_ptr())
+ }),
+ })
+ }
+}
+
+impl<T: ?Sized, B: Backend> Lock<T, B> {
+ /// Acquires the lock and gives the caller access to the data protected by it.
+ pub fn lock(&self) -> Guard<'_, T, B> {
+ // SAFETY: The constructor of the type calls `init`, so the existence of the object proves
+ // that `init` was called.
+ let state = unsafe { B::lock(self.state.get()) };
+ // SAFETY: The lock was just acquired.
+ unsafe { Guard::new(self, state) }
+ }
+}
+
+/// A lock guard.
+///
+/// Allows mutual exclusion primitives that implement the `Backend` trait to automatically unlock
+/// when a guard goes out of scope. It also provides a safe and convenient way to access the data
+/// protected by the lock.
+#[must_use = "the lock unlocks immediately when the guard is unused"]
+pub struct Guard<'a, T: ?Sized, B: Backend> {
+ pub(crate) lock: &'a Lock<T, B>,
+ pub(crate) state: B::GuardState,
+ _not_send: PhantomData<*mut ()>,
+}
+
+// SAFETY: `Guard` is sync when the data protected by the lock is also sync.
+unsafe impl<T: Sync + ?Sized, B: Backend> Sync for Guard<'_, T, B> {}
+
+impl<T: ?Sized, B: Backend> core::ops::Deref for Guard<'_, T, B> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: The caller owns the lock, so it is safe to deref the protected data.
+ unsafe { &*self.lock.data.get() }
+ }
+}
+
+impl<T: ?Sized, B: Backend> core::ops::DerefMut for Guard<'_, T, B> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ // SAFETY: The caller owns the lock, so it is safe to deref the protected data.
+ unsafe { &mut *self.lock.data.get() }
+ }
+}
+
+impl<T: ?Sized, B: Backend> Drop for Guard<'_, T, B> {
+ fn drop(&mut self) {
+ // SAFETY: The caller owns the lock, so it is safe to unlock it.
+ unsafe { B::unlock(self.lock.state.get(), &self.state) };
+ }
+}
+
+impl<'a, T: ?Sized, B: Backend> Guard<'a, T, B> {
+ /// Constructs a new immutable lock guard.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that it owns the lock.
+ pub(crate) unsafe fn new(lock: &'a Lock<T, B>, state: B::GuardState) -> Self {
+ Self {
+ lock,
+ state,
+ _not_send: PhantomData,
+ }
+ }
+}
--
2.34.1
next prev parent reply other threads:[~2023-04-11 5:46 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-04-11 5:45 [PATCH v4 01/13] rust: sync: introduce `LockClassKey` Wedson Almeida Filho
2023-04-11 5:45 ` Wedson Almeida Filho [this message]
2023-04-11 20:42 ` [PATCH v4 02/13] rust: sync: introduce `Lock` and `Guard` Gary Guo
2023-04-12 11:38 ` Wedson Almeida Filho
2023-04-14 12:02 ` Alice Ryhl
2023-04-13 8:46 ` Benno Lossin
2023-04-11 5:45 ` [PATCH v4 03/13] rust: lock: introduce `Mutex` Wedson Almeida Filho
2023-04-13 8:56 ` Benno Lossin
2023-04-11 5:45 ` [PATCH v4 04/13] locking/spinlock: introduce spin_lock_init_with_key Wedson Almeida Filho
2023-04-11 18:05 ` Wedson Almeida Filho
2023-04-12 19:14 ` Boqun Feng
2023-04-11 5:45 ` [PATCH v4 05/13] rust: lock: introduce `SpinLock` Wedson Almeida Filho
2023-04-11 5:45 ` [PATCH v4 06/13] rust: lock: add support for `Lock::lock_irqsave` Wedson Almeida Filho
2023-04-11 5:45 ` [PATCH v4 07/13] rust: lock: implement `IrqSaveBackend` for `SpinLock` Wedson Almeida Filho
2023-04-11 5:45 ` [PATCH v4 08/13] rust: introduce `ARef` Wedson Almeida Filho
2023-04-11 20:45 ` Gary Guo
2023-04-13 9:19 ` Benno Lossin
2023-04-13 17:06 ` Wedson Almeida Filho
2023-04-13 22:29 ` Benno Lossin
2023-04-14 9:00 ` Wedson Almeida Filho
2023-04-14 9:46 ` Benno Lossin
2023-04-14 14:38 ` Gary Guo
2023-04-14 17:03 ` Boqun Feng
2023-04-11 5:45 ` [PATCH v4 09/13] rust: add basic `Task` Wedson Almeida Filho
2023-04-11 20:47 ` Gary Guo
2023-04-12 11:42 ` Wedson Almeida Filho
2023-04-11 5:45 ` [PATCH v4 10/13] rust: introduce `current` Wedson Almeida Filho
2023-04-11 5:45 ` [PATCH v4 11/13] rust: lock: add `Guard::do_unlocked` Wedson Almeida Filho
2023-04-11 20:54 ` Gary Guo
2023-04-12 11:16 ` Wedson Almeida Filho
2023-04-11 21:17 ` Boqun Feng
2023-04-12 11:09 ` Wedson Almeida Filho
2023-04-12 6:25 ` Boqun Feng
2023-04-12 11:07 ` Wedson Almeida Filho
2023-04-12 14:35 ` Boqun Feng
2023-04-12 17:41 ` Wedson Almeida Filho
2023-04-11 5:45 ` [PATCH v4 12/13] rust: sync: introduce `CondVar` Wedson Almeida Filho
2023-04-14 11:55 ` Alice Ryhl
2023-04-11 5:45 ` [PATCH v4 13/13] rust: sync: introduce `LockedBy` Wedson Almeida Filho
2023-04-13 9:45 ` Benno Lossin
2023-04-11 20:35 ` [PATCH v4 01/13] rust: sync: introduce `LockClassKey` Gary Guo
2023-04-13 8:02 ` Benno Lossin
2023-04-21 23:48 ` Miguel Ojeda
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230411054543.21278-2-wedsonaf@gmail.com \
--to=wedsonaf@gmail.com \
--cc=alex.gaynor@gmail.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=gary@garyguo.net \
--cc=linux-kernel@vger.kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=walmeida@microsoft.com \
--cc=yakoyoku@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.