Types implementing one of these traits
can safely convert between an ARef<T> and an Owned<T>.
Signed-off-by: Oliver Mangold <oliver.mangold@pm.me>
---
rust/kernel/types.rs | 268 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 268 insertions(+)
diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs
index e6f3308f931d90718d405443c3034a216388e0af..0acf95d322b6a213728916f0c7f4095aa3f0e0f0 100644
--- a/rust/kernel/types.rs
+++ b/rust/kernel/types.rs
@@ -552,6 +552,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
@@ -669,6 +675,268 @@ fn drop(&mut self) {
}
}
+/// A trait for objects that can be wrapped in either one of the reference types
+/// [`Owned`] and [`ARef`].
+///
+/// # Safety
+///
+/// - The same safety requirements as for [`Ownable`] and [`RefCounted`] apply.
+/// - the uniqueness invariant of [`Owned`] is upheld until dropped.
+/// - [`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,
+/// )?;
+/// // SAFETY: we just allocated the `Foo`, thus it is valid
+/// Ok(unsafe { Owned::from_raw(NonNull::new(KBox::into_raw(result)).unwrap()) })
+/// }
+/// }
+///
+/// // SAFETY: we increment and decrement correctly 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 {
+/// // 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 conditions on implementing the trait
+ unsafe { ARef::from_raw(Owned::into_raw(this)) }
+ }
+}
+
+/// This trait allows to implement all of [`Ownable`], [`RefCounted`] and
+/// [`OwnableRefCounted`] together in a simplified way,
+/// only requiring to provide the methods [`inc_ref()`](SimpleOwnableRefCounted::inc_ref),
+/// [`dec_ref()`](SimpleOwnableRefCounted::dec_ref),
+/// and [`is_unique()`](SimpleOwnableRefCounted::is_unique).
+///
+/// For non-standard cases where conversion between [`Ownable`] and [`RefCounted`] needs
+/// or [`Ownable::release()`] and [`RefCounted::dec_ref()`] cannot be the same method,
+/// [`Ownable`], [`RefCounted`] and [`OwnableRefCounted`] should be implemented manually.
+///
+/// # Safety
+///
+/// - The same safety requirements as for [`Ownable`] and [`RefCounted`] apply.
+/// - [`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 [`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, SimpleOwnableRefCounted, Owned,
+/// };
+///
+/// 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,
+/// )?;
+/// // SAFETY: we just allocated the `Foo`, thus it is valid
+/// Ok(unsafe { Owned::from_raw(NonNull::new(KBox::into_raw(result)).unwrap()) })
+/// }
+/// }
+///
+/// // SAFETY: we implement the trait correctly by ensuring
+/// // - the `Foo` is only dropped then the refcount is zero
+/// // - `is_unique()` only returns `true` when the refcount is 1
+/// unsafe impl SimpleOwnableRefCounted 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 {
+/// // 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);
+/// }
+/// }
+/// 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 {
+ /// 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;
+ /// Increments the reference count on the object.
+ fn inc_ref(&self);
+
+ /// Decrements the reference count on the object
+ /// when the [`SimpleOwnableRefCounted`] is dropped.
+ ///
+ /// Frees the object when the count reaches zero.
+ ///
+ /// # Safety
+ ///
+ /// The safety constraints for [`RefCounted::dec_ref`] and
+ /// [`Ownable::release`] both apply to this method, as it will be used
+ /// to implement both of these traits.
+ unsafe fn dec_ref(obj: NonNull<Self>);
+}
+
+// TODO: enable this when compiler supports it (>=1.85)
+// #[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)
+ }
+ }
+}
+
+// TODO: enable this when compiler supports it (>=1.85)
+// #[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 { SimpleOwnableRefCounted::dec_ref(this) };
+ }
+}
+
+// TODO: enable this when compiler supports it (>=1.85)
+// #[diagnostic::do_not_recommend]
+// SAFETY: safe by the requirements on implementation of [`SimpleOwnableRefCounted`]
+unsafe impl<T: SimpleOwnableRefCounted> RefCounted for T {
+ fn inc_ref(&self) {
+ SimpleOwnableRefCounted::inc_ref(self);
+ }
+ unsafe fn dec_ref(this: NonNull<Self>) {
+ // SAFETY: safe by the requirements on implementation
+ // of [`SimpleOwnableRefCounted::dec_ref()`]
+ unsafe { SimpleOwnableRefCounted::dec_ref(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)
+ }
+}
+
/// A sum type that always holds either a value of type `L` or `R`.
///
/// # Examples
--
2.48.1
Hi Oliver,
Some general style nits for this and other series you may send in the
future (not a review). Please note that most may apply several times.
On Fri, Mar 7, 2025 at 11:04 AM Oliver Mangold <oliver.mangold@pm.me> wrote:
>
> Types implementing one of these traits
> can safely convert between an ARef<T> and an Owned<T>.
The wrapping is strange here, and it also happens in your code
comments. Please use the same width as the rest of the code in a file
etc.
> +/// - The same safety requirements as for [`Ownable`] and [`RefCounted`] apply.
"same" sounds like no extra requirements -- what about something like:
The safety requirements from both [.....
I wonder if we should expand/inline them, even if they come from the
supertraits.
> +/// - the uniqueness invariant of [`Owned`] is upheld until dropped.
Please use uppercase to start sentences.
> +/// // Use a KBox to handle the actual allocation
Please use Markdown for comments too, and period at the end (also for
"SAFETY: ..." comments).
> +/// // SAFETY: we implement the trait correctly by ensuring
There is no need to say "we implement the trait correctly", i.e. the
`SAFETY` tag is enough to introduce the comment; the same way we don't
say "SAFETY: the following unsafe block is correct because ..." etc.
> +/// }
> +/// fn is_unique(&self) -> bool {
Newline between items.
> +/// let foo = Foo::new().unwrap();
In general, we try to avoid showing patterns that people should avoid
when writing actual code (at least in non-hidden code) -- could we
avoid the `unwrap()`?
> +// TODO: enable this when compiler supports it (>=1.85)
> +// #[diagnostic::do_not_recommend]
I think (untested) we could conditionally enable this already and
remove the `TODO` with something like:
config RUSTC_HAS_DO_NOT_RECOMMEND
def_bool RUSTC_VERSION >= 108500
#[cfg_attr(CONFIG_RUSTC_HAS_DO_NOT_RECOMMEND, diagnostic::do_not_recommend)]
Thanks!
Cheers,
Miguel
On 250307 1416, Miguel Ojeda wrote:
> Hi Oliver,
>
> Some general style nits for this and other series you may send in the
> future (not a review). Please note that most may apply several times.
>
> On Fri, Mar 7, 2025 at 11:04 AM Oliver Mangold <oliver.mangold@pm.me> wrote:
> >
> > Types implementing one of these traits
> > can safely convert between an ARef<T> and an Owned<T>.
>
> The wrapping is strange here, and it also happens in your code
> comments. Please use the same width as the rest of the code in a file
> etc.
>
Sure, I can change that, no problem. Just to explain, I didn't give that
too much thought. I just tried to stick to the 100 chars max length.
I think I tended to try to split lines at places where it fits the
sentence structure to improve readability, but I guess you are right,
using up the maximum space is easier to the deal with
> > +/// - The same safety requirements as for [`Ownable`] and [`RefCounted`] apply.
> I wonder if we should expand/inline them, even if they come from the
> supertraits.
>
I tried to avoid copy-paste, but I can do if it is generally preferred.
Or can rustdoc include sections?
> > +/// - the uniqueness invariant of [`Owned`] is upheld until dropped.
>
> Please use uppercase to start sentences.
>
Ok. I think I mostly did, but seems I missed a few.
>
> "same" sounds like no extra requirements -- what about something like:
>
> The safety requirements from both [.....
>
> > +/// // Use a KBox to handle the actual allocation
>
> Please use Markdown for comments too, and period at the end (also for
> "SAFETY: ..." comments).
>
> > +/// // SAFETY: we implement the trait correctly by ensuring
>
> There is no need to say "we implement the trait correctly", i.e. the
> `SAFETY` tag is enough to introduce the comment; the same way we don't
> say "SAFETY: the following unsafe block is correct because ..." etc.
>
> > +/// }
> > +/// fn is_unique(&self) -> bool {
>
> Newline between items.
>
> > +/// let foo = Foo::new().unwrap();
>
> In general, we try to avoid showing patterns that people should avoid
> when writing actual code (at least in non-hidden code) -- could we
> avoid the `unwrap()`?
Sure, I will fix all of the above in the next release.
> > +// TODO: enable this when compiler supports it (>=1.85)
> > +// #[diagnostic::do_not_recommend]
>
On 250309 2247, Miguel Ojeda wrote:
>
> Oliver: I am sending a quick patch explaining this -- please feel free
> to pick it up in your series.
Thanks. I agree it is better not having to change this after compiler upgrade.
Best regards,
Oliver
On Fri, Mar 07, 2025 at 02:16:32PM +0100, Miguel Ojeda wrote: > > +// TODO: enable this when compiler supports it (>=1.85) > > +// #[diagnostic::do_not_recommend] > > I think (untested) we could conditionally enable this already and > remove the `TODO` with something like: > > config RUSTC_HAS_DO_NOT_RECOMMEND > def_bool RUSTC_VERSION >= 108500 > > #[cfg_attr(CONFIG_RUSTC_HAS_DO_NOT_RECOMMEND, diagnostic::do_not_recommend)] It's only a warning on older compilers. We could just add this in lib.rs: #![allow(unknown_or_malformed_diagnostic_attributes)] Alice
On Fri, Mar 7, 2025 at 2:28 PM Alice Ryhl <aliceryhl@google.com> wrote: > > It's only a warning on older compilers. We could just add this in > lib.rs: > > #![allow(unknown_or_malformed_diagnostic_attributes)] That is OK too, though we would lose the check for typos until we upgrade the minimum. Hmm... Since we have nowadays the way to create those conditions easily, I think it is fine to do it conditionally. Cheers, Miguel
On Fri, Mar 07, 2025 at 02:53:30PM +0100, Miguel Ojeda wrote: > On Fri, Mar 7, 2025 at 2:28 PM Alice Ryhl <aliceryhl@google.com> wrote: > > > > It's only a warning on older compilers. We could just add this in > > lib.rs: > > > > #![allow(unknown_or_malformed_diagnostic_attributes)] > > That is OK too, though we would lose the check for typos until we > upgrade the minimum. > > Hmm... Since we have nowadays the way to create those conditions > easily, I think it is fine to do it conditionally. Another possibility is to make the allow conditional. Alice
On Fri, Mar 7, 2025 at 4:58 PM Alice Ryhl <aliceryhl@google.com> wrote: > > Another possibility is to make the allow conditional. That sounds fine to me -- we would lose checking for those not in the latest version, but we would catch the mistakes on our side eventually. The advantage of using the `allow` is mostly less churn later on, and perhaps fewer mistakes due to that. In terms of lines, it would still be the same since they are single lines. Oliver: I am sending a quick patch explaining this -- please feel free to pick it up in your series. Thanks! Cheers, Miguel
Rust 1.85.0 (current stable version) stabilized [1]
`#[diagnostic::do_not_recommend]` [2].
In order to use it across all supported Rust versions, introduce a new
Kconfig symbol for it.
This allows to perform conditional compilation based on it, e.g. on the
use site to enable the attribute:
#[cfg_attr(RUSTC_HAS_DO_NOT_RECOMMEND, diagnostic::do_not_recommend)]
impl A for i32 {}
An alternative would have been to `allow` the following warning:
#![allow(unknown_or_malformed_diagnostic_attributes)]
However, that would lose the checking for typos across all versions,
which we do not want to lose.
One can also use the Kconfig symbol to allow the warning in older
compilers instead, to avoid repeating the `cfg_attr` line above in all
use sites:
#![cfg_attr(
not(RUSTC_HAS_DO_NOT_RECOMMEND),
expect(unknown_or_malformed_diagnostic_attributes)
)]
That still loses the checking for typos in older versions, but we still
keep it in newer ones, thus we should still catch mistakes eventually.
In this case we can promote it to `expect` as shown above, so that we do
not forget to remove these lines if we stop using the attribute somewhere.
Link: https://github.com/rust-lang/rust/pull/132056 [1]
Link: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-diagnosticdo_not_recommend-attribute [2]
Link: https://lore.kernel.org/rust-for-linux/CANiq72mYfhuRWkjomb1vOMMPOaxvdS6qjfVLAwxUw6ecdqyh2A@mail.gmail.com/
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
---
init/Kconfig | 3 +++
1 file changed, 3 insertions(+)
diff --git a/init/Kconfig b/init/Kconfig
index d0d021b3fa3b..213b4cc9310a 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -132,6 +132,9 @@ config CC_HAS_COUNTED_BY
config RUSTC_HAS_COERCE_POINTEE
def_bool RUSTC_VERSION >= 108400
+config RUSTC_HAS_DO_NOT_RECOMMEND
+ def_bool RUSTC_VERSION >= 108500
+
config PAHOLE_VERSION
int
default $(shell,$(srctree)/scripts/pahole-version.sh $(PAHOLE))
base-commit: 7eb172143d5508b4da468ed59ee857c6e5e01da6
--
2.48.1
© 2016 - 2026 Red Hat, Inc.