[PATCH v5 2/4] rust: clk: implement Clone for Clk<T>

Daniel Almeida <[email protected]>
Newsgroups org.kernel.vger.linux-pwm,org.freedesktop.lists.dri-devel,org.infradead.lists.linux-riscv,org.kernel.vger.linux-clk,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pm,org.kernel.vger.rust-for-linux
Message-ID <[email protected]>
The type-state pattern makes state transitions consume the Clk value,
which means a single Clk cannot be shared between users that need to
hold the clock in different states, nor can a driver keep a long-lived
Clk<Prepared> around while temporarily enabling it for scoped sections.

Implement Clone for Clk<T>: each clone is an independent view of the
same underlying clock, in the same state, owning its own
clk_prepare()/clk_enable() counts, e.g.:

	let enabled_clk = prepared_clk.clone().enable()?;

	// Do stuff that requires the clock to be enabled.

	// enabled_clk goes out of scope and releases the counts it
	// owns; the clock remains prepared through prepared_clk.

Since struct clk is not refcounted on the C side, share it between
clones by wrapping the pointer in an Arc'd RawClk, whose drop
implementation calls clk_put() exactly once, when the last clone goes
out of scope. This costs one small allocation per clk_get(), which was
deemed negligible when compared against the pre-existing indirections
in the clk framework.

Cloning a prepared (or enabled) Clk calls clk_prepare() (and
clk_enable()) on the underlying clock. These calls cannot fail here:
the value being cloned already holds a count of each, so the C side
only increments the respective counts. This is what makes an infallible
Clone implementation possible.

The DISABLE_ON_DROP and UNPREPARE_ON_DROP constants are renamed to
ENABLED and PREPARED respectively, since they now describe the state
for both Drop and Clone. #[repr(transparent)] is removed from Clk<T>,
as the layout is no longer transparent over the raw clk pointer.

This was proposed and discussed on the list in response to v3 [1], with
a prototype by Boris Brezillon [2].

Link: https://lore.kernel.org/r/20260203113902.501e5803@fedora [1]
Link: https://gitlab.freedesktop.org/bbrezillon/linux/-/commit/d5d04da4f4f6192b6e6760d5f861c69596c7d837 [2]
Signed-off-by: Daniel Almeida <[email protected]>
---
 rust/kernel/clk.rs | 181 ++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 139 insertions(+), 42 deletions(-)

diff --git a/rust/kernel/clk.rs b/rust/kernel/clk.rs
index a9edfdf9db68..dd5fd656271e 100644
--- a/rust/kernel/clk.rs
+++ b/rust/kernel/clk.rs
@@ -83,6 +83,7 @@ mod common_clk {
         device::{Bound, Device},
         error::{from_err_ptr, to_result, Result},
         prelude::*,
+        sync::Arc,
     };
 
     use core::{marker::PhantomData, mem::ManuallyDrop, ptr};
@@ -97,11 +98,11 @@ impl Sealed for super::Enabled {}
 
     /// A trait representing the different states that a [`Clk`] can be in.
     pub trait ClkState: private::Sealed {
-        /// Whether the clock should be disabled when dropped.
-        const DISABLE_ON_DROP: bool;
+        /// Whether the clock is enabled in this state.
+        const ENABLED: bool;
 
-        /// Whether the clock should be unprepared when dropped.
-        const UNPREPARE_ON_DROP: bool;
+        /// Whether the clock is prepared in this state.
+        const PREPARED: bool;
     }
 
     /// A state where the [`Clk`] is not prepared and not enabled.
@@ -114,18 +115,18 @@ pub trait ClkState: private::Sealed {
     pub struct Enabled;
 
     impl ClkState for Unprepared {
-        const DISABLE_ON_DROP: bool = false;
-        const UNPREPARE_ON_DROP: bool = false;
+        const ENABLED: bool = false;
+        const PREPARED: bool = false;
     }
 
     impl ClkState for Prepared {
-        const DISABLE_ON_DROP: bool = false;
-        const UNPREPARE_ON_DROP: bool = true;
+        const ENABLED: bool = false;
+        const PREPARED: bool = true;
     }
 
     impl ClkState for Enabled {
-        const DISABLE_ON_DROP: bool = true;
-        const UNPREPARE_ON_DROP: bool = true;
+        const ENABLED: bool = true;
+        const PREPARED: bool = true;
     }
 
     /// An error that can occur when trying to convert a [`Clk`] between states.
@@ -183,13 +184,15 @@ fn from(err: Error<State>) -> Self {
     /// portion of the kernel or a `NULL` pointer.
     ///
     /// Instances of this type are reference-counted. Calling [`Clk::get`] ensures that the
-    /// allocation remains valid for the lifetime of the [`Clk`].
+    /// allocation remains valid for the lifetime of the [`Clk`] and of all its clones: the
+    /// underlying [`struct clk`] is obtained through a single call to `clk_get()`, which is
+    /// balanced by a single call to `clk_put()` when the last clone is dropped.
     ///
-    /// The [`Prepared`] state is associated with a single count of
-    /// `clk_prepare()`, and the [`Enabled`] state is associated with a single
-    /// count of both `clk_prepare()` and `clk_enable()`.
-    ///
-    /// All states are associated with a single count of `clk_get()`.
+    /// Each [`Clk`] value owns its own state counts: the [`Prepared`] state is
+    /// associated with a single count of `clk_prepare()`, and the [`Enabled`]
+    /// state is associated with a single count of both `clk_prepare()` and
+    /// `clk_enable()`. These counts are released when the value transitions to
+    /// a lower state or is dropped.
     ///
     /// # Examples
     ///
@@ -268,13 +271,57 @@ fn from(err: Error<State>) -> Self {
     /// }
     /// ```
     ///
+    /// Cloning a [`Clk`] yields an independent view of the same underlying
+    /// clock, in the same state, owning its own state counts. This lets a
+    /// driver keep e.g. a long-lived [`Clk<Prepared>`] around and temporarily
+    /// enable a clone of it:
+    ///
+    /// ```
+    /// use kernel::clk::{Clk, Enabled, Prepared};
+    /// use kernel::error::Result;
+    ///
+    /// fn use_clk(prepared_clk: &Clk<Prepared>) -> Result {
+    ///     let enabled_clk: Clk<Enabled> = prepared_clk.clone().enable()?;
+    ///
+    ///     // Do something that requires the clock to be enabled.
+    ///
+    ///     // `enabled_clk` is dropped here and releases its own counts; the
+    ///     // clock remains prepared through `prepared_clk`.
+    ///     Ok(())
+    /// }
+    /// ```
+    ///
+    /// Note that cloning a [`Clk<Prepared>`] or a [`Clk<Enabled>`] may sleep.
+    ///
     /// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html
-    #[repr(transparent)]
     pub struct Clk<T: ClkState> {
-        inner: *mut bindings::clk,
+        inner: Arc<RawClk>,
         _phantom: core::marker::PhantomData<T>,
     }
 
+    /// Owns a single `clk_get()` reference to a [`struct clk`].
+    ///
+    /// This is shared by every clone of a [`Clk`], so that `clk_put()` is
+    /// called exactly once, when the last clone is dropped.
+    ///
+    /// # Invariants
+    ///
+    /// The wrapped pointer is either a pointer to a valid [`struct clk`]
+    /// obtained through `clk_get()` or one of its variants, or `NULL`.
+    ///
+    /// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html
+    #[repr(transparent)]
+    struct RawClk(*mut bindings::clk);
+
+    impl Drop for RawClk {
+        #[inline]
+        fn drop(&mut self) {
+            // SAFETY: By the type invariants, `self.0` is a valid argument for
+            // [`clk_put`], which also accepts a `NULL` pointer.
+            unsafe { bindings::clk_put(self.0) };
+        }
+    }
+
     // SAFETY: It is safe to call `clk_put` on another thread than where `clk_get` was called.
     unsafe impl<T: ClkState> Send for Clk<T> {}
 
@@ -311,11 +358,14 @@ pub(crate) fn get_unbound(dev: &Device, name: Option<&CStr>) -> Result<Clk<Unpre
 
             // SAFETY: It is safe to call [`clk_get`] for a valid device pointer
             // and any `con_id`, including NULL.
-            let inner = from_err_ptr(unsafe { bindings::clk_get(dev.as_raw(), con_id) })?;
+            let clk = from_err_ptr(unsafe { bindings::clk_get(dev.as_raw(), con_id) })?;
 
-            // INVARIANT: The reference-count is decremented when [`Clk`] goes out of scope.
+            // INVARIANT: The single `clk_get()` reference is owned by `RawClk`,
+            // which releases it when the last clone of this [`Clk`] goes out of
+            // scope. If the allocation fails, `Arc::new` drops the `RawClk`,
+            // releasing the reference.
             Ok(Self {
-                inner,
+                inner: Arc::new(RawClk(clk), GFP_KERNEL)?,
                 _phantom: PhantomData,
             })
         }
@@ -329,11 +379,14 @@ pub fn get_optional(dev: &Device<Bound>, name: Option<&CStr>) -> Result<Clk<Unpr
 
             // SAFETY: It is safe to call [`clk_get`] for a valid device pointer
             // and any `con_id`, including NULL.
-            let inner = from_err_ptr(unsafe { bindings::clk_get_optional(dev.as_raw(), con_id) })?;
+            let clk = from_err_ptr(unsafe { bindings::clk_get_optional(dev.as_raw(), con_id) })?;
 
-            // INVARIANT: The reference-count is decremented when [`Clk`] goes out of scope.
+            // INVARIANT: The single `clk_get()` reference is owned by `RawClk`,
+            // which releases it when the last clone of this [`Clk`] goes out of
+            // scope. If the allocation fails, `Arc::new` drops the `RawClk`,
+            // releasing the reference.
             Ok(Self {
-                inner,
+                inner: Arc::new(RawClk(clk), GFP_KERNEL)?,
                 _phantom: PhantomData,
             })
         }
@@ -356,7 +409,10 @@ pub fn prepare(self) -> Result<Clk<Prepared>, Error<Unprepared>> {
                 // `Clk<Prepared>` owns a single count of it, which is released
                 // when it leaves the [`Prepared`] state.
                 .map(|()| Clk {
-                    inner: clk.inner,
+                    // SAFETY: `clk` is wrapped in `ManuallyDrop`, so its `Arc`
+                    // reference is never released; moving it out here keeps the
+                    // reference count balanced.
+                    inner: unsafe { ptr::read(&clk.inner) },
                     _phantom: PhantomData,
                 })
                 .map_err(|error| Error {
@@ -404,9 +460,12 @@ pub fn unprepare(self) -> Clk<Unprepared> {
             unsafe { bindings::clk_unprepare(clk.as_raw()) }
 
             // INVARIANT: The `clk_prepare()` count was released above, so the
-            // returned `Clk<Unprepared>` owns only the `clk_get()` count.
+            // returned `Clk<Unprepared>` owns only the `clk_get()` reference.
             Clk {
-                inner: clk.inner,
+                // SAFETY: `clk` is wrapped in `ManuallyDrop`, so its `Arc`
+                // reference is never released; moving it out here keeps the
+                // reference count balanced.
+                inner: unsafe { ptr::read(&clk.inner) },
                 _phantom: PhantomData,
             }
         }
@@ -429,7 +488,10 @@ pub fn enable(self) -> Result<Clk<Enabled>, Error<Prepared>> {
                 // `Clk<Enabled>` owns a single count of it, which is released
                 // when it leaves the [`Enabled`] state.
                 .map(|()| Clk {
-                    inner: clk.inner,
+                    // SAFETY: `clk` is wrapped in `ManuallyDrop`, so its `Arc`
+                    // reference is never released; moving it out here keeps the
+                    // reference count balanced.
+                    inner: unsafe { ptr::read(&clk.inner) },
                     _phantom: PhantomData,
                 })
                 .map_err(|error| Error {
@@ -474,7 +536,10 @@ pub fn with_enabled<R>(&self, cb: impl FnOnce(&Clk<Enabled>) -> R) -> Result<R>
             //
             // INVARIANT: The clock is enabled for the lifetime of `enabled`.
             let enabled = ManuallyDrop::new(Clk::<Enabled> {
-                inner: self.inner,
+                // SAFETY: The duplicate `Arc` reference is wrapped in
+                // `ManuallyDrop` and thus never released, keeping the reference
+                // count balanced.
+                inner: unsafe { ptr::read(&self.inner) },
                 _phantom: PhantomData,
             });
 
@@ -527,10 +592,13 @@ pub fn disable(self) -> Clk<Prepared> {
             unsafe { bindings::clk_disable(clk.as_raw()) };
 
             // INVARIANT: The `clk_enable()` count was released above, so the
-            // returned `Clk<Prepared>` owns the `clk_get()` and `clk_prepare()`
-            // counts.
+            // returned `Clk<Prepared>` owns the `clk_get()` reference and the
+            // `clk_prepare()` count.
             Clk {
-                inner: clk.inner,
+                // SAFETY: `clk` is wrapped in `ManuallyDrop`, so its `Arc`
+                // reference is never released; moving it out here keeps the
+                // reference count balanced.
+                inner: unsafe { ptr::read(&clk.inner) },
                 _phantom: PhantomData,
             }
         }
@@ -540,7 +608,7 @@ impl<T: ClkState> Clk<T> {
         /// Obtain the raw [`struct clk`] pointer.
         #[inline]
         pub fn as_raw(&self) -> *mut bindings::clk {
-            self.inner
+            self.inner.0
         }
 
         /// Get clock's rate.
@@ -573,21 +641,50 @@ pub fn set_rate(&self, rate: Hertz) -> Result {
 
     impl<T: ClkState> Drop for Clk<T> {
         fn drop(&mut self) {
-            if T::DISABLE_ON_DROP {
-                // SAFETY: By the type invariants, self.as_raw() is a valid argument for
-                // [`clk_disable`].
+            if T::ENABLED {
+                // SAFETY: By the type invariants, `self.as_raw()` is a valid
+                // argument for [`clk_disable`].
                 unsafe { bindings::clk_disable(self.as_raw()) };
             }
 
-            if T::UNPREPARE_ON_DROP {
-                // SAFETY: By the type invariants, self.as_raw() is a valid argument for
-                // [`clk_unprepare`].
+            if T::PREPARED {
+                // SAFETY: By the type invariants, `self.as_raw()` is a valid
+                // argument for [`clk_unprepare`].
                 unsafe { bindings::clk_unprepare(self.as_raw()) };
             }
 
-            // SAFETY: By the type invariants, self.as_raw() is a valid argument for
-            // [`clk_put`].
-            unsafe { bindings::clk_put(self.as_raw()) };
+            // The `clk_get()` reference is owned by `RawClk`: it is released
+            // by its drop implementation when the last clone goes out of scope.
+        }
+    }
+
+    impl<T: ClkState> Clone for Clk<T> {
+        #[inline]
+        fn clone(&self) -> Self {
+            if T::PREPARED {
+                // SAFETY: By the type invariants, `self.as_raw()` is a valid
+                // argument for [`clk_prepare`]. `self` already holds a
+                // `clk_prepare()` count, so the underlying clock is already
+                // prepared and this call only increments the prepare count:
+                // it cannot fail.
+                unsafe { bindings::clk_prepare(self.as_raw()) };
+            }
+
+            if T::ENABLED {
+                // SAFETY: By the type invariants, `self.as_raw()` is a valid
+                // argument for [`clk_enable`]. `self` already holds a
+                // `clk_enable()` count, so the underlying clock is already
+                // enabled and this call only increments the enable count: it
+                // cannot fail.
+                unsafe { bindings::clk_enable(self.as_raw()) };
+            }
+
+            // INVARIANT: The new value owns the state counts acquired above
+            // and shares the `clk_get()` reference through the `Arc`.
+            Self {
+                inner: self.inner.clone(),
+                _phantom: PhantomData,
+            }
         }
     }
 }

-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.