From: Alice Ryhl <aliceryhl@google.com>
To: Oliver Mangold <oliver.mangold@pm.me>
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>,
"Benno Lossin" <benno.lossin@proton.me>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Trevor Gross" <tmgross@umich.edu>,
"Asahi Lina" <lina@asahilina.net>,
rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v10 5/5] rust: Add OwnableRefCounted and SimpleOwnableRefCounted
Date: Fri, 2 May 2025 11:43:15 +0000 [thread overview]
Message-ID: <aBSv0w5x8qhBfOM8@google.com> (raw)
In-Reply-To: <20250502-unique-ref-v10-5-25de64c0307f@pm.me>
On Fri, May 02, 2025 at 09:03:04AM +0000, Oliver Mangold wrote:
> Types implementing one of these traits can safely convert between an
> ARef<T> and an Owned<T>.
>
> This is useful for types which generally are accessed through an ARef
> but have methods which can only safely be called when the reference is
> unique, like e.g. `block::mq::Request::end_ok()`.
>
> Signed-off-by: Oliver Mangold <oliver.mangold@pm.me>
> Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
> ---
> rust/kernel/types.rs | 8 +-
> rust/kernel/types/ownable.rs | 244 +++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 251 insertions(+), 1 deletion(-)
>
> diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
> index 33d2b4e4a87b991c6d934f4e8d2c6c71a15b1bcb..3a58905599eb9acb0e701c97bd92d3b93b9515cf 100644
> --- a/rust/kernel/types.rs
> +++ b/rust/kernel/types.rs
> @@ -12,7 +12,7 @@
> use pin_init::{PinInit, Zeroable};
>
> pub mod ownable;
> -pub use ownable::{Ownable, OwnableMut, Owned};
> +pub use ownable::{Ownable, OwnableMut, OwnableRefCounted, Owned, SimpleOwnableRefCounted};
>
> /// Used to transfer ownership to and from foreign (non-Rust) languages.
> ///
> @@ -544,6 +544,12 @@ fn from(b: &T) -> Self {
> }
> }
>
> +impl<T: OwnableRefCounted> From<Owned<T>> for ARef<T> {
> + fn from(b: Owned<T>) -> Self {
> + T::into_shared(b)
> + }
> +}
> +
> impl<T: RefCounted> Drop for ARef<T> {
> fn drop(&mut self) {
> // SAFETY: The type invariants guarantee that the `ARef` owns the reference we're about to
> diff --git a/rust/kernel/types/ownable.rs b/rust/kernel/types/ownable.rs
> index 52e7a69019f1e2bbbe3cf715651b67a5a5c7c13d..b79459d07870ea4fa4f5df0c7565ac72d65e2c53 100644
> --- a/rust/kernel/types/ownable.rs
> +++ b/rust/kernel/types/ownable.rs
> @@ -2,6 +2,7 @@
>
> //! Owned reference types.
>
> +use crate::types::{ARef, RefCounted};
> use core::{
> marker::PhantomData,
> mem::ManuallyDrop,
> @@ -115,3 +116,246 @@ fn drop(&mut self) {
> unsafe { T::release(self.ptr) };
> }
> }
> +
> +/// A trait for objects that can be wrapped in either one of the reference types [`Owned`] and
> +/// [`ARef`].
> +///
> +/// # Safety
> +///
> +/// Implementers must ensure that:
> +///
> +/// - Both the safety requirements for [`Ownable`] and [`RefCounted`] are fulfilled.
> +/// - [`try_from_shared()`](OwnableRefCounted::into_shared) only returns an [`Owned`] if exactly
> +/// one [`ARef`] exists.
> +/// - [`into_shared()`](OwnableRefCounted::into_shared) set the reference count to the value which
> +/// the returned [`ARef`] expects for an object with a single reference
> +/// in existence. This implies that if [`into_shared()`](OwnableRefCounted::into_shared) is left
> +/// on the default implementation, which just rewraps the underlying object, the reference count
> +/// needs not to be modified when converting a [`Owned`] to an [`ARef`].
> +///
> +/// # Examples
> +///
> +/// A minimal example implementation of [`OwnableRefCounted`], [`Ownable`] and its usage with
> +/// [`ARef`] and [`Owned`] looks like this:
> +///
> +/// ```
> +/// # #![expect(clippy::disallowed_names)]
> +/// use core::cell::Cell;
> +/// use core::ptr::NonNull;
> +/// use kernel::alloc::{flags, kbox::KBox, AllocError};
> +/// use kernel::types::{
> +/// ARef, RefCounted, Owned, Ownable, OwnableRefCounted,
> +/// };
> +///
> +/// struct Foo {
> +/// refcount: Cell<usize>,
> +/// }
> +///
> +/// impl Foo {
> +/// fn new() -> Result<Owned<Self>, AllocError> {
> +/// // Use a `KBox` to handle the actual allocation.
> +/// let result = KBox::new(
> +/// Foo {
> +/// refcount: Cell::new(1),
> +/// },
> +/// flags::GFP_KERNEL,
> +/// )?;
> +/// let result = NonNull::new(KBox::into_raw(result))
> +/// .expect("Raw pointer to newly allocation KBox is null, this should never happen.");
> +/// // SAFETY: We just allocated the `Foo`, thus it is valid.
> +/// Ok(unsafe { Owned::from_raw(result) })
> +/// }
> +/// }
> +///
> +/// // SAFETY: We increment and decrement each time the respective function is called and only free
> +/// // the `Foo` when the refcount reaches zero.
> +/// unsafe impl RefCounted for Foo {
> +/// fn inc_ref(&self) {
> +/// self.refcount.replace(self.refcount.get() + 1);
> +/// }
> +///
> +/// unsafe fn dec_ref(this: NonNull<Self>) {
> +/// // SAFETY: The underlying object is always valid when the function is called.
> +/// let refcount = unsafe { &this.as_ref().refcount };
> +/// let new_refcount = refcount.get() - 1;
> +/// if new_refcount == 0 {
> +/// // The `Foo` will be dropped when `KBox` goes out of scope.
> +/// // SAFETY: The `Box<Foo>` is still alive as the old refcount is 1.
> +/// unsafe { KBox::from_raw(this.as_ptr()) };
> +/// } else {
> +/// refcount.replace(new_refcount);
> +/// }
> +/// }
> +/// }
> +///
> +/// // SAFETY: We only convert into an `Owned` when the refcount is 1.
> +/// unsafe impl OwnableRefCounted for Foo {
> +/// fn try_from_shared(this: ARef<Self>) -> Result<Owned<Self>, ARef<Self>> {
> +/// if this.refcount.get() == 1 {
> +/// // SAFETY: The `Foo` is still alive as the refcount is 1.
> +/// Ok(unsafe { Owned::from_raw(ARef::into_raw(this)) })
> +/// } else {
> +/// Err(this)
> +/// }
> +/// }
> +/// }
> +///
> +/// // SAFETY: We are not `AlwaysRefCounted`.
> +/// unsafe impl Ownable for Foo {
> +/// unsafe fn release(this: NonNull<Self>) {
> +/// // SAFETY: Using `dec_ref()` from `RefCounted` to release is okay, as the refcount is
> +/// // always 1 for an `Owned<Foo>`.
> +/// unsafe{ Foo::dec_ref(this) };
> +/// }
> +/// }
> +///
> +/// let foo = Foo::new().unwrap();
> +/// let mut foo = ARef::from(foo);
> +/// {
> +/// let bar = foo.clone();
> +/// assert!(Owned::try_from(bar).is_err());
> +/// }
> +/// assert!(Owned::try_from(foo).is_ok());
> +/// ```
> +pub unsafe trait OwnableRefCounted: RefCounted + Ownable + Sized {
> + /// Checks if the [`ARef`] is unique and convert it to an [`Owned`] it that is that case.
> + /// Otherwise it returns again an [`ARef`] to the same underlying object.
> + fn try_from_shared(this: ARef<Self>) -> Result<Owned<Self>, ARef<Self>>;
> +
> + /// Converts the [`Owned`] into an [`ARef`].
> + fn into_shared(this: Owned<Self>) -> ARef<Self> {
> + // SAFETY: Safe by the requirements on implementing the trait.
> + unsafe { ARef::from_raw(Owned::into_raw(this)) }
> + }
> +}
> +
> +/// This trait allows to implement [`Ownable`] and [`OwnableRefCounted`] together in a simplified
> +/// way, only requiring to implement [`RefCounted`] and providing the method
> +/// [`is_unique()`](SimpleOwnableRefCounted::is_unique).
> +///
> +/// For non-standard cases where conversion between [`Ownable`] and [`RefCounted`] does not allow
> +/// [`Ownable::release()`] and [`RefCounted::dec_ref()`] to be the same method, [`Ownable`]
> +/// and [`OwnableRefCounted`] should be implemented separately.
> +///
> +/// # Safety
> +///
> +/// Implementers must ensure that:
> +///
> +/// - The safety requirements for [`Ownable`] are fulfilled and [`RefCounted::dec_ref()`] can
> +/// be used for [`Ownable::release()`].
> +/// - [`is_unique`](SimpleOwnableRefCounted::is_unique) must only return `true` in case only one
> +/// [`ARef`] exists and it is impossible for one to be obtained other than by cloning an existing
> +/// [`ARef`] or converting an [`Owned`] to an [`ARef`].
> +/// - It is safe to convert an unique [`ARef`] into an [`Owned`] simply by re-wrapping the
> +/// underlying object without modifying the refcount.
> +///
> +/// # Examples
> +///
> +/// A minimal example implementation of [`RefCounted`] and [`SimpleOwnableRefCounted`]
> +/// and its usage with [`ARef`] and [`Owned`] looks like this:
> +///
> +/// ```
> +/// # #![expect(clippy::disallowed_names)]
> +/// use core::cell::Cell;
> +/// use core::ptr::NonNull;
> +/// use kernel::alloc::{flags, kbox::KBox, AllocError};
> +/// use kernel::types::{
> +/// ARef, Owned, RefCounted, SimpleOwnableRefCounted,
> +/// };
> +///
> +/// struct Foo {
> +/// refcount: Cell<usize>,
> +/// }
> +///
> +/// impl Foo {
> +/// fn new() -> Result<Owned<Self>, AllocError> {
> +/// // Use a KBox to handle the actual allocation.
> +/// let result = KBox::new(
> +/// Foo {
> +/// refcount: Cell::new(1),
> +/// },
> +/// flags::GFP_KERNEL,
> +/// )?;
> +/// let result = NonNull::new(KBox::into_raw(result))
> +/// .expect("Raw pointer to newly allocation KBox is null, this should never happen.");
> +/// // SAFETY: We just allocated the `Foo`, thus it is valid.
> +/// Ok(unsafe { Owned::from_raw(result) })
> +/// }
> +/// }
> +///
> +/// // SAFETY: we ensure that:
> +/// // - The `Foo` is only dropped when the refcount is zero.
> +/// // - `is_unique()` only returns `true` when the refcount is 1.
> +/// unsafe impl RefCounted for Foo {
> +/// fn inc_ref(&self) {
> +/// self.refcount.replace(self.refcount.get() + 1);
> +/// }
> +///
> +/// unsafe fn dec_ref(this: NonNull<Self>) {
> +/// // SAFETY: The underlying object is always valid when the function is called.
> +/// let refcount = unsafe { &this.as_ref().refcount };
> +/// let new_refcount = refcount.get() - 1;
> +/// if new_refcount == 0 {
> +/// // The `Foo` will be dropped when KBox goes out of scope.
> +/// // SAFETY: The `Box<Foo>` is still alive as the old refcount is 1.
> +/// unsafe { KBox::from_raw(this.as_ptr()) };
> +/// } else {
> +/// refcount.replace(new_refcount);
> +/// }
> +/// }
> +/// }
> +///
> +/// // SAFETY: we ensure that:
> +/// // - `is_unique()` only returns `true` when the refcount is 1.
> +/// unsafe impl SimpleOwnableRefCounted for Foo {
> +/// fn is_unique(&self) -> bool {
> +/// self.refcount.get() == 1
> +/// }
> +/// }
> +///
> +/// let foo = Foo::new().unwrap();
> +/// let mut foo = ARef::from(foo);
> +/// {
> +/// let bar = foo.clone();
> +/// assert!(Owned::try_from(bar).is_err());
> +/// }
> +/// assert!(Owned::try_from(foo).is_ok());
> +/// ```
> +pub unsafe trait SimpleOwnableRefCounted: RefCounted {
> + /// Checks if exactly one [`ARef`] to the object exists. In case the object is [`Sync`], the
> + /// check needs to be race-free.
> + fn is_unique(&self) -> bool;
> +}
> +
> +#[cfg_attr(RUSTC_HAS_DO_NOT_RECOMMEND, diagnostic::do_not_recommend)]
> +// SAFETY: Safe by the requirements on implementation of [`SimpleOwnableRefCounted`].
> +unsafe impl<T: SimpleOwnableRefCounted> OwnableRefCounted for T {
> + fn try_from_shared(this: ARef<Self>) -> Result<Owned<Self>, ARef<Self>> {
> + if T::is_unique(&*this) {
> + // SAFETY: Safe by the requirements on implementation of [`SimpleOwnable`].
> + Ok(unsafe { Owned::from_raw(ARef::into_raw(this)) })
> + } else {
> + Err(this)
> + }
> + }
> +}
> +
> +#[cfg_attr(RUSTC_HAS_DO_NOT_RECOMMEND, diagnostic::do_not_recommend)]
> +// SAFETY: Safe by the requirements on implementation of [`SimpleOwnableRefCounted`].
> +unsafe impl<T: SimpleOwnableRefCounted> Ownable for T {
> + unsafe fn release(this: NonNull<Self>) {
> + // SAFETY: Safe by the requirements on implementation of
> + // [`SimpleOwnableRefCounted::dec_ref()`].
> + unsafe { RefCounted::dec_ref(this) };
> + }
> +}
I wonder if this is too limiting. It will limit our ability to write
other blanket impls for Ownable and OwnableRefCounted. Using e.g. a
derive macro might be better?
Alice
next prev parent reply other threads:[~2025-05-02 11:43 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-05-02 9:02 [PATCH v10 0/5] New trait OwnableRefCounted for ARef<->Owned conversion Oliver Mangold
2025-05-02 9:02 ` [PATCH v10 1/5] rust: types: Add Ownable/Owned types Oliver Mangold
2025-05-02 9:57 ` Andreas Hindborg
2025-06-16 11:43 ` Oliver Mangold
2025-06-17 11:42 ` Andreas Hindborg
2025-05-02 11:29 ` Alice Ryhl
2025-05-06 11:20 ` Andreas Hindborg
2025-05-07 6:20 ` Alice Ryhl
2025-05-08 12:24 ` Andreas Hindborg
2025-05-14 9:32 ` Benno Lossin
2025-06-17 9:58 ` Oliver Mangold
2025-06-18 21:22 ` Benno Lossin
2025-06-20 7:01 ` Oliver Mangold
2025-06-20 8:09 ` Benno Lossin
2025-06-18 9:34 ` Oliver Mangold
2025-06-18 21:19 ` Benno Lossin
2025-06-19 9:33 ` Andreas Hindborg
2025-06-19 12:18 ` Benno Lossin
2025-05-02 9:02 ` [PATCH v10 2/5] rust: Rename AlwaysRefCounted to RefCounted Oliver Mangold
2025-05-02 10:10 ` Andreas Hindborg
2025-05-02 11:32 ` Alice Ryhl
2025-06-16 11:56 ` Oliver Mangold
2025-06-16 12:13 ` Alice Ryhl
2025-05-02 9:02 ` [PATCH v10 3/5] rust: Add missing SAFETY documentation for ARef example Oliver Mangold
2025-05-02 10:41 ` Andreas Hindborg
2025-05-02 11:12 ` Oliver Mangold
2025-05-02 12:01 ` Andreas Hindborg
2025-05-02 9:02 ` [PATCH v10 4/5] rust: kbuild: provide `RUSTC_HAS_DO_NOT_RECOMMEND` symbol Oliver Mangold
2025-05-02 9:03 ` [PATCH v10 5/5] rust: Add OwnableRefCounted and SimpleOwnableRefCounted Oliver Mangold
2025-05-02 11:43 ` Alice Ryhl [this message]
2025-05-06 11:42 ` Oliver Mangold
2025-05-07 6:19 ` Alice Ryhl
2025-06-13 13:10 ` [PATCH v10 0/5] New trait OwnableRefCounted for ARef<->Owned conversion Andreas Hindborg
2025-06-13 13:27 ` Oliver Mangold
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=aBSv0w5x8qhBfOM8@google.com \
--to=aliceryhl@google.com \
--cc=a.hindborg@kernel.org \
--cc=alex.gaynor@gmail.com \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=gary@garyguo.net \
--cc=lina@asahilina.net \
--cc=linux-kernel@vger.kernel.org \
--cc=ojeda@kernel.org \
--cc=oliver.mangold@pm.me \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.