Rust for Linux List
 help / color / mirror / Atom feed
From: Boqun Feng <boqun.feng@gmail.com>
To: Alice Ryhl <aliceryhl@google.com>
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
	"Peter Zijlstra" <peterz@infradead.org>,
	"Ingo Molnar" <mingo@redhat.com>, "Will Deacon" <will@kernel.org>,
	"Waiman Long" <longman@redhat.com>, "Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <benno.lossin@proton.me>,
	rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
	"Andreas Hindborg" <a.hindborg@kernel.org>
Subject: Re: [PATCH v5] rust: add global lock support
Date: Mon, 21 Oct 2024 08:22:54 -0700	[thread overview]
Message-ID: <ZxZxzjEaSZ8e_6mn@boqun-archlinux> (raw)
In-Reply-To: <20241021-static-mutex-v5-1-8d118a6a99b7@google.com>

On Mon, Oct 21, 2024 at 01:17:23PM +0000, Alice Ryhl wrote:
[...]
> +///
> +/// A global mutex used to protect all instances of a given struct.
> +///
> +/// ```
> +/// # mod ex {
> +/// # use kernel::prelude::*;
> +/// kernel::sync::global_lock! {
> +///     // SAFETY: Initialized in module initializer before first use.
> +///     unsafe(uninit) static MY_MUTEX: Mutex<(), Guard = MyGuard, LockedBy = LockedByMyMutex> = ();

Thanks! This looks much better now ;-)

But I still want to get rid of "LockedBy=", so I've tried and seems it
works, please see the below diff on top of your patch, I think it's
better because:

* Users don't to pick up the names for the locked_by type ;-)
* It moves a significant amount of code out of macros.
* By having:

    struct MyStruct {
        my_counter: GlobalLockedBy<MyGuard, u32>,
    }

  , it's much clear for users to see which guard is used to protected
  `my_counter`.

I prefer this way. Any concern about doing this?

Regards,
Boqun

> +/// }
> +///
> +/// /// All instances of this struct are protected by `MY_MUTEX`.
> +/// struct MyStruct {
> +///     my_counter: LockedByMyMutex<u32>,
> +/// }
> +///
> +/// impl MyStruct {
> +///     /// Increment the counter in this instance.
> +///     ///
> +///     /// The caller must hold the `MY_MUTEX` mutex.
> +///     fn increment(&self, guard: &mut MyGuard) -> u32 {
> +///         let my_counter = self.my_counter.as_mut(guard);
> +///         *my_counter += 1;
> +///         *my_counter
> +///     }
> +/// }
> +///
> +/// impl kernel::Module for MyModule {
> +///     fn init(_module: &'static ThisModule) -> Result<Self> {
> +///         // SAFETY: called exactly once
> +///         unsafe { MY_MUTEX.init() };
> +///
> +///         Ok(MyModule {})
> +///     }
> +/// }
> +/// # struct MyModule {}
> +/// # }
> +/// ```

[...]

----------------------------------->8
diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
index 9b3b401f3fcc..0d227541faef 100644
--- a/rust/kernel/sync/lock.rs
+++ b/rust/kernel/sync/lock.rs
@@ -15,6 +15,8 @@
 
 pub(super) mod global;
 
+pub use global::{GlobalGuard, GlobalLockedBy};
+
 /// The "backend" of a lock.
 ///
 /// It is the actual implementation of the lock, without the need to repeat patterns used in all
diff --git a/rust/kernel/sync/lock/global.rs b/rust/kernel/sync/lock/global.rs
index 803f19db4545..bef188938d5a 100644
--- a/rust/kernel/sync/lock/global.rs
+++ b/rust/kernel/sync/lock/global.rs
@@ -4,6 +4,63 @@
 
 //! Support for defining statics containing locks.
 
+use core::{cell::UnsafeCell, marker::PhantomData};
+
+/// A marker for the guard type of a global lock.
+///
+/// # Safety
+///
+/// Implementers must guarantee that the type is a guard type of a global lock, that is,  the
+/// existence of the object represents a unique global lock is held.
+pub unsafe trait GlobalGuard { }
+
+/// Data protected by a global lock.
+pub struct GlobalLockedBy<G: GlobalGuard, T: ?Sized>(PhantomData<fn(&G)>, UnsafeCell<T>);
+
+impl<G: GlobalGuard, T> GlobalLockedBy<G, T> {
+    /// Creates a new data.
+    pub const fn new(val: T) -> Self {
+        Self(PhantomData, UnsafeCell::new(val))
+    }
+}
+
+impl<G: GlobalGuard, T: ?Sized> GlobalLockedBy<G, T> {
+    /// Returns the immutable reference to the data with the lock held.
+    ///
+    /// With an immutable reference of the [`GlobalGuard`], the function is safe because the
+    /// corresponding global lock is held.
+    pub fn as_ref(&self, _guard: &G) -> &T {
+        // SAFETY: Per the safety requirement of `GlobalGuard`, the lock is held, and with the
+        // shared reference of the guard, it's safe to return an immutable reference to the object.
+        unsafe { &*self.1.get() }
+    }
+
+    /// Returns the mutable reference to the data with the lock held.
+    ///
+    /// With a mutable reference of the [`GlobalGuard`], the function is safe because the
+    /// corresponding global lock is held, and the exclusive reference of the guard guarantees the
+    /// exclusive access of `T`.
+    #[allow(clippy::mut_from_ref)]
+    pub fn as_mut(&self, _guard: &mut G) -> &mut T {
+        // SAFETY: Per the safety requirement of `GlobalGuard`, the lock is held, and with the
+        // exclusive reference of the guard, it's safe to return a mutable reference to the object.
+        unsafe { &mut *self.1.get() }
+    }
+
+    /// Returns the mutable references to the data.
+    pub fn get_mut(&mut self) -> &mut T {
+        self.1.get_mut()
+    }
+}
+
+// SAFETY: `GlobalLockedBy` can be transferred across thread boundaries iff the data it protects
+// can.
+unsafe impl<G: GlobalGuard, T: ?Sized + Send> Send for GlobalLockedBy<G, T> {}
+
+// SAFETY: `GlobalLockedBy` serialises the interior mutability it provides, so it is `Sync` as long
+// as the data it protects is `Send`.
+unsafe impl<G: GlobalGuard, T: ?Sized + Send> Sync for GlobalLockedBy<G, T> {}
+
 /// Defines a global lock.
 ///
 /// The global mutex must be initialized before first use. Usually this is done by calling `init`
@@ -44,14 +101,15 @@
 /// ```
 /// # mod ex {
 /// # use kernel::prelude::*;
+/// use kernel::sync::lock::GlobalLockedBy;
 /// kernel::sync::global_lock! {
 ///     // SAFETY: Initialized in module initializer before first use.
-///     unsafe(uninit) static MY_MUTEX: Mutex<(), Guard = MyGuard, LockedBy = LockedByMyMutex> = ();
+///     unsafe(uninit) static MY_MUTEX: Mutex<(), Guard = MyGuard> = ();
 /// }
 ///
 /// /// All instances of this struct are protected by `MY_MUTEX`.
 /// struct MyStruct {
-///     my_counter: LockedByMyMutex<u32>,
+///     my_counter: GlobalLockedBy<MyGuard, u32>,
 /// }
 ///
 /// impl MyStruct {
@@ -81,7 +139,7 @@ macro_rules! global_lock {
     {
         $(#[$meta:meta])* $pub:vis
         unsafe(uninit) static $name:ident: $kind:ident<$valuety:ty
-            $(, Guard = $guard:ident $(, LockedBy = $locked_by:ident)?)?
+            $(, Guard = $guard:ident)?
         > = $value:expr;
     } => {
         $crate::macros::paste! {
@@ -167,44 +225,13 @@ fn deref_mut(&mut self) -> &mut Val {
                     }
                 }
 
-                $(
-                pub struct $locked_by<T: ?Sized>(::core::cell::UnsafeCell<T>);
-
-                // SAFETY: `LockedBy` can be transferred across thread boundaries iff the data it
-                // protects can.
-                unsafe impl<T: ?Sized + Send> Send for $locked_by<T> {}
-
-                // SAFETY: `LockedBy` serialises the interior mutability it provides, so it is `Sync` as long as the
-                // data it protects is `Send`.
-                unsafe impl<T: ?Sized + Send> Sync for $locked_by<T> {}
-
-                impl<T> $locked_by<T> {
-                    pub fn new(val: T) -> Self {
-                        Self(::core::cell::UnsafeCell::new(val))
-                    }
-                }
-
-                impl<T: ?Sized> $locked_by<T> {
-                    pub fn as_ref<'a>(&'a self, _guard: &'a $guard) -> &'a T {
-                        // SAFETY: The lock is globally unique, so there can only be one guard.
-                        unsafe { &*self.0.get() }
-                    }
-
-                    pub fn as_mut<'a>(&'a self, _guard: &'a mut $guard) -> &'a mut T {
-                        // SAFETY: The lock is globally unique, so there can only be one guard.
-                        unsafe { &mut *self.0.get() }
-                    }
-
-                    pub fn get_mut(&mut self) -> &mut T {
-                        self.0.get_mut()
-                    }
-                }
-                )?)?
+                // SAFETY: `$guard` is a guard type for a unique global lock.
+                unsafe impl $crate::sync::lock::GlobalGuard for $guard {}
+                )?
             }
 
             use [< __static_lock_mod_ $name >]::[< __static_lock_wrapper_ $name >];
-            $( $pub use [< __static_lock_mod_ $name >]::$guard;
-            $( $pub use [< __static_lock_mod_ $name >]::$locked_by; )?)?
+            $( $pub use [< __static_lock_mod_ $name >]::$guard;)?
 
             $(#[$meta])*
             #[allow(private_interfaces)]

  reply	other threads:[~2024-10-21 15:23 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-10-21 13:17 [PATCH v5] rust: add global lock support Alice Ryhl
2024-10-21 15:22 ` Boqun Feng [this message]
2024-10-22 12:46   ` Alice Ryhl
2024-10-22 16:44     ` Boqun Feng
2024-10-22 17:24       ` Alice Ryhl
2024-10-23 13:39         ` Alice Ryhl
2024-10-22 11:04 ` Alice Ryhl
  -- strict thread matches above, loose matches on Subject: below --
2024-10-23 13:17 Alice Ryhl
2024-10-23 13:19 ` Alice Ryhl

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=ZxZxzjEaSZ8e_6mn@boqun-archlinux \
    --to=boqun.feng@gmail.com \
    --cc=a.hindborg@kernel.org \
    --cc=aliceryhl@google.com \
    --cc=benno.lossin@proton.me \
    --cc=bjorn3_gh@protonmail.com \
    --cc=gary@garyguo.net \
    --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=will@kernel.org \
    /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