[PATCH v5 2/4] rust: clk: implement Clone for Clk<T>
Daniel Almeida
daniel.almeida at collabora.com
Mon Jul 6 07:37:13 PDT 2026
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 <daniel.almeida at collabora.com>
---
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
More information about the linux-riscv
mailing list