From: Lyude Paul <lyude@redhat.com>
To: rust-for-linux@vger.kernel.org
Cc: "Danilo Krummrich" <dakr@redhat.com>,
airlied@redhat.com, "Ingo Molnar" <mingo@redhat.com>,
"Will Deacon" <will@kernel.org>,
"Waiman Long" <longman@redhat.com>,
"Peter Zijlstra" <peterz@infradead.org>,
"Thomas Gleixner" <tglx@linutronix.de>,
linux-kernel@vger.kernel.org, "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>,
"Benno Lossin" <benno.lossin@proton.me>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Filipe Xavier" <felipe_life@live.com>,
"Martin Rodriguez Reboredo" <yakoyoku@gmail.com>,
"Danilo Krummrich" <dakr@kernel.org>,
"Valentin Obst" <kernel@valentinobst.de>,
"Wedson Almeida Filho" <walmeida@microsoft.com>
Subject: [PATCH v7 3/3] rust: sync: Add SpinLockIrq
Date: Fri, 18 Oct 2024 19:13:52 -0400 [thread overview]
Message-ID: <20241018231621.474601-5-lyude@redhat.com> (raw)
In-Reply-To: <20241018231621.474601-2-lyude@redhat.com>
A variant of SpinLock that is expected to be used in noirq contexts, and
thus requires that the user provide an kernel::irq::IrqDisabled to prove
they are in such a context upon lock acquisition. This is the rust
equivalent of spin_lock_irqsave()/spin_lock_irqrestore().
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
V2:
* s/IrqSpinLock/SpinLockIrq/
* Implement `lock::Backend` now that we have `Context`
* Add missing periods
* Make sure rustdoc examples compile correctly
* Add documentation suggestions
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
rust/helpers/spinlock.c | 14 +++
rust/kernel/sync.rs | 2 +-
rust/kernel/sync/lock/spinlock.rs | 145 ++++++++++++++++++++++++++++++
3 files changed, 160 insertions(+), 1 deletion(-)
diff --git a/rust/helpers/spinlock.c b/rust/helpers/spinlock.c
index 775ed4d549aef..f4108d2d78648 100644
--- a/rust/helpers/spinlock.c
+++ b/rust/helpers/spinlock.c
@@ -27,3 +27,17 @@ int rust_helper_spin_trylock(spinlock_t *lock)
{
return spin_trylock(lock);
}
+
+size_t rust_helper_spin_lock_irqsave(spinlock_t *lock)
+{
+ size_t flags = 0;
+
+ spin_lock_irqsave(lock, flags);
+
+ return flags;
+}
+
+void rust_helper_spin_unlock_irqrestore(spinlock_t *lock, size_t flags)
+{
+ spin_unlock_irqrestore(lock, flags);
+}
diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
index 0ab20975a3b5d..b028ee325f2a6 100644
--- a/rust/kernel/sync.rs
+++ b/rust/kernel/sync.rs
@@ -15,7 +15,7 @@
pub use arc::{Arc, ArcBorrow, UniqueArc};
pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult};
pub use lock::mutex::{new_mutex, Mutex};
-pub use lock::spinlock::{new_spinlock, SpinLock};
+pub use lock::spinlock::{new_spinlock, new_spinlock_irq, SpinLock, SpinLockIrq};
pub use locked_by::LockedBy;
/// Represents a lockdep class. It's a wrapper around C's `lock_class_key`.
diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs
index 9fbfd96ffba3e..d342ee954f6a8 100644
--- a/rust/kernel/sync/lock/spinlock.rs
+++ b/rust/kernel/sync/lock/spinlock.rs
@@ -3,6 +3,7 @@
//! A kernel spinlock.
//!
//! This module allows Rust code to use the kernel's `spinlock_t`.
+use kernel::local_irq::*;
/// Creates a [`SpinLock`] initialiser with the given name and a newly-created lock class.
///
@@ -116,6 +117,123 @@ unsafe fn unlock(ptr: *mut Self::State, _guard_state: &Self::GuardState) {
unsafe { bindings::spin_unlock(ptr) }
}
+ unsafe fn try_lock(ptr: *mut Self::State) -> Option<Self::GuardState> {
+ // SAFETY: The `ptr` pointer is guaranteed to be valid and initialized before use.
+ let result = unsafe { bindings::spin_trylock(ptr) };
+
+ (result != 0).then_some(())
+ }
+}
+
+/// Creates a [`SpinLockIrq`] initialiser with the given name and a newly-created lock class.
+///
+/// It uses the name if one is given, otherwise it generates one based on the file name and line
+/// number.
+#[macro_export]
+macro_rules! new_spinlock_irq {
+ ($inner:expr $(, $name:literal)? $(,)?) => {
+ $crate::sync::SpinLockIrq::new(
+ $inner, $crate::optional_name!($($name)?), $crate::static_lock_class!())
+ };
+}
+pub use new_spinlock_irq;
+
+/// A spinlock that may be acquired when interrupts are disabled.
+///
+/// A version of [`SpinLock`] that can only be used in contexts where interrupts for the local CPU
+/// are disabled. It requires that the user acquiring the lock provide proof that interrupts are
+/// disabled through [`IrqDisabled`].
+///
+/// For more info, see [`SpinLock`].
+///
+/// # Examples
+///
+/// The following example shows how to declare, allocate initialise and access a struct (`Example`)
+/// that contains two inner structs of type `Inner` that are protected by separate spinlocks.
+///
+/// ```
+/// use kernel::{
+/// sync::{new_spinlock_irq, SpinLockIrq},
+/// local_irq::IrqDisabled
+/// };
+///
+/// struct Inner {
+/// a: u32,
+/// b: u32,
+/// }
+///
+/// #[pin_data]
+/// struct Example {
+/// c: u32,
+/// #[pin]
+/// first: SpinLockIrq<Inner>,
+/// #[pin]
+/// second: SpinLockIrq<Inner>,
+/// }
+///
+/// impl Example {
+/// fn new() -> impl PinInit<Self> {
+/// pin_init!(Self {
+/// c: 10,
+/// first <- new_spinlock_irq!(Inner { a: 20, b: 30 }),
+/// second <- new_spinlock_irq!(Inner { a: 10, b: 20 }),
+/// })
+/// }
+/// }
+///
+/// // Allocate a boxed `Example`
+/// let example = KBox::pin_init(Example::new(), GFP_KERNEL)?;
+///
+/// // Accessing an `Inner` from a context where we don't have a `LocalInterruptsDisabled` token
+/// // already.
+/// let bb = example.first.lock_with_new(|first, irq| {
+/// assert_eq!(example.c, 10);
+/// assert_eq!(first.a, 20);
+///
+/// // Since we already have a `LocalInterruptsDisabled` token, we can reuse it to acquire the
+/// // second lock. This allows us to skip changing the local interrupt state unnecessarily on
+/// // non-PREEMPT_RT kernels.
+/// let second = example.second.lock_with(irq);
+/// assert_eq!(second.a, 10);
+///
+/// (first.b, second.b)
+/// });
+///
+/// assert_eq!(bb, (30, 20));
+/// # Ok::<(), Error>(())
+/// ```
+pub type SpinLockIrq<T> = super::Lock<T, SpinLockIrqBackend>;
+
+/// A kernel `spinlock_t` lock backend that is acquired in no-irq contexts.
+pub struct SpinLockIrqBackend;
+
+unsafe impl super::Backend for SpinLockIrqBackend {
+ type State = bindings::spinlock_t;
+ type GuardState = ();
+ type Context<'a> = IrqDisabled<'a>;
+
+ unsafe fn init(
+ ptr: *mut Self::State,
+ name: *const core::ffi::c_char,
+ key: *mut bindings::lock_class_key,
+ ) {
+ // SAFETY: The safety requirements ensure that `ptr` is valid for writes, and `name` and
+ // `key` are valid for read indefinitely.
+ unsafe { bindings::__spin_lock_init(ptr, name, key) }
+ }
+
+ unsafe fn lock(ptr: *mut Self::State) -> Self::GuardState {
+ // SAFETY: The safety requirements of this function ensure that `ptr` points to valid
+ // memory, and that it has been initialised before.
+ unsafe { bindings::spin_lock(ptr) }
+ }
+
+ unsafe fn unlock(ptr: *mut Self::State, _guard_state: &Self::GuardState) {
+ // SAFETY: The safety requirements of this function ensure that `ptr` is valid and that the
+ // caller is the owner of the spinlock.
+ unsafe { bindings::spin_unlock(ptr) }
+ }
+
unsafe fn try_lock(ptr: *mut Self::State) -> Option<Self::GuardState> {
// SAFETY: The `ptr` pointer is guaranteed to be valid and initialized before use.
let result = unsafe { bindings::spin_trylock(ptr) };
@@ -127,3 +245,30 @@ unsafe fn try_lock(ptr: *mut Self::State) -> Option<Self::GuardState> {
}
}
}
+
+impl super::BackendWithContext for SpinLockIrqBackend {
+ type ContextState = usize;
+
+ unsafe fn lock_with_context_saved<'a>(
+ ptr: *mut Self::State,
+ ) -> (Self::Context<'a>, Self::ContextState, Self::GuardState) {
+ // SAFETY: The safety requirements of this function ensure that `ptr` points to valid
+ // memory, and that it has been initialised before.
+ let flags = unsafe { bindings::spin_lock_irqsave(ptr) };
+
+ // SAFETY: We just disabled interrupts above
+ let context = unsafe { IrqDisabled::new() };
+
+ (context, flags, ())
+ }
+
+ unsafe fn unlock_with_context_restored(
+ ptr: *mut Self::State,
+ _guard_state: &Self::GuardState,
+ context_state: Self::ContextState,
+ ) {
+ // SAFETY: The safety requirements of this function ensure that `ptr` is valid and that the
+ // caller is the owner of the spinlock.
+ unsafe { bindings::spin_unlock_irqrestore(ptr, context_state) }
+ }
+}
--
2.47.0
next prev parent reply other threads:[~2024-10-18 23:17 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-10-18 23:13 [PATCH v7 0/3] rust: Add local_irq abstraction, SpinLockIrq Lyude Paul
2024-10-18 23:13 ` [PATCH v7 1/3] rust: Introduce local_irq module Lyude Paul
2024-10-18 23:13 ` [PATCH v7 2/3] rust: sync: Introduce lock::Backend::Context and lock::BackendWithContext Lyude Paul
2024-10-18 23:13 ` Lyude Paul [this message]
2024-10-18 23:20 ` ignore [PATCH v7 0/3] rust: Add local_irq abstraction, SpinLockIrq Lyude Paul
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=20241018231621.474601-5-lyude@redhat.com \
--to=lyude@redhat.com \
--cc=a.hindborg@kernel.org \
--cc=airlied@redhat.com \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=dakr@kernel.org \
--cc=dakr@redhat.com \
--cc=felipe_life@live.com \
--cc=gary@garyguo.net \
--cc=kernel@valentinobst.de \
--cc=linux-kernel@vger.kernel.org \
--cc=longman@redhat.com \
--cc=mingo@redhat.com \
--cc=ojeda@kernel.org \
--cc=peterz@infradead.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tglx@linutronix.de \
--cc=tmgross@umich.edu \
--cc=walmeida@microsoft.com \
--cc=will@kernel.org \
--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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).