linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
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>
Subject: [PATCH v3 11/13] rust: lock: add `Guard::do_unlocked`
Date: Sat,  8 Apr 2023 04:53:38 -0300	[thread overview]
Message-ID: <20230408075340.25237-11-wedsonaf@gmail.com> (raw)
In-Reply-To: <20230408075340.25237-1-wedsonaf@gmail.com>

From: Wedson Almeida Filho <walmeida@microsoft.com>

It releases the lock, executes some function provided by the caller,
then reacquires the lock. This is preparation for the implementation of
condvars, which will sleep after between unlocking and relocking.

We need an explicit `relock` method for primitives like `SpinLock` that
have an irqsave variant: we use the guard state to determine if the lock
was originally acquired with the regular `lock` function or
`lock_irqsave`.

Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
---
v1 -> v2: No changes
v2 -> v3: No changes

 rust/kernel/sync/lock.rs          | 25 +++++++++++++++++++++++++
 rust/kernel/sync/lock/spinlock.rs | 17 +++++++++++++++--
 2 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
index 690429561f0e..a2c654ec5bd3 100644
--- a/rust/kernel/sync/lock.rs
+++ b/rust/kernel/sync/lock.rs
@@ -22,6 +22,9 @@ pub mod spinlock;
 ///
 /// - 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`.
+/// - Implementers must also ensure that `relock` uses the same locking method as the original
+/// lock operation. For example, it should disable interrupts if [`IrqSaveBackend::lock_irqsave`]
+/// is used.
 pub unsafe trait Backend {
     /// The state required by the lock.
     type State;
@@ -55,6 +58,17 @@ pub unsafe trait Backend {
     ///
     /// It must only be called by the current owner of the lock.
     unsafe fn unlock(ptr: *mut Self::State, guard_state: &Self::GuardState);
+
+    /// Reacquires the lock, making the caller its owner.
+    ///
+    /// # Safety
+    ///
+    /// Callers must ensure that `state` comes from a previous call to [`Backend::lock`] (or
+    /// variant) that has been unlocked with [`Backend::unlock`] and will be relocked now.
+    unsafe fn relock(ptr: *mut Self::State, guard_state: &mut Self::GuardState) {
+        // SAFETY: The safety requirements ensure that the lock is initialised.
+        *guard_state = unsafe { Self::lock(ptr) };
+    }
 }
 
 /// The "backend" of a lock that supports the irq-save variant.
@@ -164,6 +178,17 @@ pub struct Guard<'a, T: ?Sized, B: Backend> {
 // 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> Guard<'_, T, B> {
+    #[allow(dead_code)]
+    pub(crate) fn do_unlocked(&mut self, cb: impl FnOnce()) {
+        // SAFETY: The caller owns the lock, so it is safe to unlock it.
+        unsafe { B::unlock(self.lock.state.get(), &self.state) };
+        cb();
+        // SAFETY: The lock was just unlocked above and is being relocked now.
+        unsafe { B::relock(self.lock.state.get(), &mut self.state) };
+    }
+}
+
 impl<T: ?Sized, B: Backend> core::ops::Deref for Guard<'_, T, B> {
     type Target = T;
 
diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs
index 34dec09a97c0..e2a2f68e6d93 100644
--- a/rust/kernel/sync/lock/spinlock.rs
+++ b/rust/kernel/sync/lock/spinlock.rs
@@ -4,6 +4,7 @@
 //!
 //! This module allows Rust code to use the kernel's `spinlock_t`.
 
+use super::IrqSaveBackend;
 use crate::bindings;
 
 /// Creates a [`SpinLock`] initialiser with the given name and a newly-created lock class.
@@ -95,7 +96,8 @@ pub type SpinLock<T> = super::Lock<T, SpinLockBackend>;
 /// A kernel `spinlock_t` lock backend.
 pub struct SpinLockBackend;
 
-// SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion.
+// SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion. `relock` uses the
+// same scheme as `unlock` to figure out which locking method was used originally.
 unsafe impl super::Backend for SpinLockBackend {
     type State = bindings::spinlock_t;
     type GuardState = Option<core::ffi::c_ulong>;
@@ -127,13 +129,24 @@ unsafe impl super::Backend for SpinLockBackend {
             None => unsafe { bindings::spin_unlock(ptr) },
         }
     }
+
+    unsafe fn relock(ptr: *mut Self::State, guard_state: &mut Self::GuardState) {
+        let _ = match guard_state {
+            // SAFETY: The safety requiments of this function ensure that `ptr` has been
+            // initialised.
+            None => unsafe { Self::lock(ptr) },
+            // SAFETY: The safety requiments of this function ensure that `ptr` has been
+            // initialised.
+            Some(_) => unsafe { Self::lock_irqsave(ptr) },
+        };
+    }
 }
 
 // SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion. We use the `irqsave`
 // variant of the C lock acquisition functions to disable interrupts and retrieve the original
 // interrupt state, and the `irqrestore` variant of the lock release functions to restore the state
 // in `unlock` -- we use the guard context to determine which method was used to acquire the lock.
-unsafe impl super::IrqSaveBackend for SpinLockBackend {
+unsafe impl IrqSaveBackend for SpinLockBackend {
     unsafe fn lock_irqsave(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.
-- 
2.34.1


  parent reply	other threads:[~2023-04-08  7:55 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-04-08  7:53 [PATCH v3 01/13] rust: sync: introduce `LockClassKey` Wedson Almeida Filho
2023-04-08  7:53 ` [PATCH v3 02/13] rust: sync: introduce `Lock` and `Guard` Wedson Almeida Filho
2023-04-09 16:47   ` Martin Rodriguez Reboredo
2023-04-11  3:01     ` Wedson Almeida Filho
2023-04-08  7:53 ` [PATCH v3 03/13] rust: lock: introduce `Mutex` Wedson Almeida Filho
2023-04-09 16:47   ` Martin Rodriguez Reboredo
2023-04-08  7:53 ` [PATCH v3 04/13] locking/spinlock: introduce spin_lock_init_with_key Wedson Almeida Filho
2023-04-09 16:47   ` Martin Rodriguez Reboredo
2023-04-08  7:53 ` [PATCH v3 05/13] rust: lock: introduce `SpinLock` Wedson Almeida Filho
2023-04-09 16:48   ` Martin Rodriguez Reboredo
2023-04-08  7:53 ` [PATCH v3 06/13] rust: lock: add support for `Lock::lock_irqsave` Wedson Almeida Filho
2023-04-09 16:48   ` Martin Rodriguez Reboredo
2023-04-08  7:53 ` [PATCH v3 07/13] rust: lock: implement `IrqSaveBackend` for `SpinLock` Wedson Almeida Filho
2023-04-09 16:48   ` Martin Rodriguez Reboredo
2023-04-08  7:53 ` [PATCH v3 08/13] rust: introduce `ARef` Wedson Almeida Filho
2023-04-09 16:48   ` Martin Rodriguez Reboredo
2023-04-08  7:53 ` [PATCH v3 09/13] rust: add basic `Task` Wedson Almeida Filho
2023-04-09 16:48   ` Martin Rodriguez Reboredo
2023-04-08  7:53 ` [PATCH v3 10/13] rust: introduce `current` Wedson Almeida Filho
2023-04-09 16:48   ` Martin Rodriguez Reboredo
2023-04-10 18:04   ` Benno Lossin
2023-04-11  3:08     ` Wedson Almeida Filho
2023-04-08  7:53 ` Wedson Almeida Filho [this message]
2023-04-09 16:49   ` [PATCH v3 11/13] rust: lock: add `Guard::do_unlocked` Martin Rodriguez Reboredo
2023-04-08  7:53 ` [PATCH v3 12/13] rust: sync: introduce `CondVar` Wedson Almeida Filho
2023-04-09 16:49   ` Martin Rodriguez Reboredo
2023-04-11  2:59     ` Wedson Almeida Filho
2023-04-08  7:53 ` [PATCH v3 13/13] rust: sync: introduce `LockedBy` Wedson Almeida Filho
2023-04-09 16:49   ` Martin Rodriguez Reboredo
2023-04-10 17:46   ` Boqun Feng
2023-04-10 18:13     ` Boqun Feng
2023-04-10 19:52   ` Benno Lossin
2023-04-11  2:57     ` Wedson Almeida Filho
2023-04-09 16:46 ` [PATCH v3 01/13] rust: sync: introduce `LockClassKey` Martin Rodriguez Reboredo

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=20230408075340.25237-11-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 \
    /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).