All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Benno Lossin" <lossin@kernel.org>
To: "Oliver Mangold" <oliver.mangold@pm.me>,
	"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>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Asahi Lina" <lina+kernel@asahilina.net>
Cc: <rust-for-linux@vger.kernel.org>, <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v11 4/4] rust: Add `OwnableRefCounted`
Date: Wed, 02 Jul 2025 15:24:48 +0200	[thread overview]
Message-ID: <DB1LPFAX66WG.1QL5JDCWI7RN4@kernel.org> (raw)
In-Reply-To: <20250618-unique-ref-v11-4-49eadcdc0aa6@pm.me>

On Wed Jun 18, 2025 at 2:27 PM CEST, Oliver Mangold wrote:
> diff --git a/rust/kernel/types/ownable.rs b/rust/kernel/types/ownable.rs
> index 80cd990f6601767aa5a742a6c0997f4f67d06453..b5626dead6bb25ea76a0ae577db1b130308d98b1 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,
> @@ -18,8 +19,9 @@
>  ///
>  /// Note: Implementing this trait allows types to be wrapped in an [`Owned<Self>`]. This does not
>  /// provide reference counting but represents a unique, owned reference. If reference counting is
> -/// required [`RefCounted`](crate::types::RefCounted) should be implemented which allows types to be
> -/// wrapped in an [`ARef<Self>`](crate::types::ARef).
> +/// required [`RefCounted`] should be implemented which allows types to be wrapped in an
> +/// [`ARef<Self>`]. Implementing the trait [`OwnableRefCounted`] allows to convert between unique
> +/// and shared references (i.e. [`Owned<Self>`] and [`ARef<Self>`]).

This change should probably be in the earlier patch.

>  ///
>  /// # Safety
>  ///
> @@ -132,3 +134,124 @@ 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:
> +///
> +/// - [`try_from_shared()`](OwnableRefCounted::into_shared) only returns an [`Owned<Self>`] if
> +///   exactly one [`ARef<Self>`] exists.

This shouldn't be required?

> +/// - [`into_shared()`](OwnableRefCounted::into_shared) set the reference count to the value which
> +///   the returned [`ARef<Self>`] 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 an [`Owned<Self>`] to an [`ARef<Self>`].

This also seems pretty weird...

I feel like `OwnableRefCounted` is essentially just a compatibility
condition between `Ownable` and `RefCounted`. It ensures that the
ownership declared in `Ownable` corresponds to exactly one refcount
declared in `RefCounted`.

That being said, I think a `RefCounted` *always* canonically is
`Ownable` by the following impl:

    unsafe impl<T: RefCounted> Ownable for T {
        unsafe fn release(this: NonNull<Self>) {
            T::dec_ref(this)
        }
    }

So I don't think that we need this trait at all?

> +///
> +/// # Examples

If we're having an example here, then we should also have on on `Owned`.

> +///
> +/// 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.");

I'm not really convinced that an example using `KBox` is a good one...
Maybe we should just have a local invisible `bindings` module that
exposes a `-> *mut foo`. (internally it can just create a KBox`)

> +///         // SAFETY: We just allocated the `Foo`, thus it is valid.

This isn't going through all the requirements on `from_raw`...

---
Cheers,
Benno

> +///         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)) }
> +    }
> +}
> +
> +impl<T: OwnableRefCounted> TryFrom<ARef<T>> for Owned<T> {
> +    type Error = ARef<T>;
> +    /// Tries to convert the [`ARef`] to an [`Owned`] by calling
> +    /// [`try_from_shared()`](OwnableRefCounted::try_from_shared). In case the [`ARef`] is not
> +    /// unique, it returns again an [`ARef`] to the same underlying object.
> +    fn try_from(b: ARef<T>) -> Result<Owned<T>, Self::Error> {
> +        T::try_from_shared(b)
> +    }
> +}


  reply	other threads:[~2025-07-02 13:24 UTC|newest]

Thread overview: 56+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <OYpTDi4YYXiWvLG3nO_8_WKsgOl9KOpun9l3a34m0jza6nmEWDCLTldSwCfZ2PRRprjXqGmrgSL2JN8rPOQH8Q==@protonmail.internalid>
2025-06-18 12:27 ` [PATCH v11 0/4] New trait OwnableRefCounted for ARef<->Owned conversion Oliver Mangold
2025-06-18 12:27   ` [PATCH v11 1/4] rust: types: Add Ownable/Owned types Oliver Mangold
2025-07-02 11:03     ` Benno Lossin
2025-07-07  6:58       ` Oliver Mangold
2025-07-07  9:23         ` Benno Lossin
2025-07-08  9:56           ` Oliver Mangold
2025-07-08 10:16             ` Miguel Ojeda
2025-07-08 13:06               ` Benno Lossin
2025-07-08 18:30                 ` Andreas Hindborg
2025-07-08 19:18                   ` Benno Lossin
2025-07-09  8:53                     ` Andreas Hindborg
2025-07-09  9:11                       ` Benno Lossin
2025-07-08 13:22               ` Andreas Hindborg
2025-07-08 14:53                 ` Benno Lossin
2025-07-08 15:00             ` Benno Lossin
2025-07-07 12:26         ` Miguel Ojeda
2025-09-25  8:31       ` Oliver Mangold
2025-08-18 12:46     ` Andreas Hindborg
2025-08-18 13:04       ` Oliver Mangold
2025-08-18 22:27         ` Benno Lossin
2025-08-19  6:04           ` Oliver Mangold
2025-08-19  8:26             ` Benno Lossin
2025-08-19  8:45               ` Oliver Mangold
2025-08-19  9:00                 ` Andreas Hindborg
2025-08-19 17:15                   ` Benno Lossin
2025-08-20 10:48                     ` Andreas Hindborg
2025-08-19  8:53               ` Andreas Hindborg
2025-08-19 17:13                 ` Benno Lossin
2025-08-19 18:28                   ` Andreas Hindborg
2025-08-20  6:02                   ` Oliver Mangold
2025-08-20  7:41                     ` Benno Lossin
2025-08-20  7:43                       ` Oliver Mangold
2025-08-20 10:51                         ` Andreas Hindborg
2025-06-18 12:27   ` [PATCH v11 2/4] rust: Split `AlwaysRefCounted` into two traits Oliver Mangold
2025-06-19  3:15     ` kernel test robot
2025-07-02 11:23     ` Benno Lossin
2025-07-07  7:42       ` Oliver Mangold
2025-07-07  9:27         ` Benno Lossin
2025-06-18 12:27   ` [PATCH v11 3/4] rust: Add missing SAFETY documentation for `ARef` example Oliver Mangold
2025-06-18 12:27   ` [PATCH v11 4/4] rust: Add `OwnableRefCounted` Oliver Mangold
2025-07-02 13:24     ` Benno Lossin [this message]
2025-07-07  8:07       ` Oliver Mangold
2025-07-07  9:33         ` Benno Lossin
2025-07-07 11:12           ` Andreas Hindborg
2025-07-07 11:47             ` Benno Lossin
2025-07-07 13:21               ` Andreas Hindborg
2025-07-07 15:39                 ` Benno Lossin
2025-07-08 13:15                   ` Andreas Hindborg
2025-07-08 14:50                     ` Benno Lossin
2025-07-08 15:35                       ` Andreas Hindborg
2025-07-08  9:36           ` Oliver Mangold
2025-07-08 13:42             ` Benno Lossin
2025-08-05 17:23   ` [PATCH v11 0/4] New trait OwnableRefCounted for ARef<->Owned conversion Danilo Krummrich
2025-08-06  5:56     ` Oliver Mangold
2025-08-15 10:12   ` Andreas Hindborg
2025-08-18  5:59     ` 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=DB1LPFAX66WG.1QL5JDCWI7RN4@kernel.org \
    --to=lossin@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=gary@garyguo.net \
    --cc=lina+kernel@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.