From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-4322.protonmail.ch (mail-4322.protonmail.ch [185.70.43.22]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2C2592288C6 for ; Mon, 10 Mar 2025 10:57:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.22 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741604280; cv=none; b=Y9tWo/1470RwReC3n12MuVATME9DuhNwXOmqeaBd3aVrPOBg9iM3y9g8oWuo8+WpwldGi68W8GeLk4Lk8+wp7MUhB88qaSo/CqfA5FCdLMNo49doYEs0LJe0Bw/ng2UoPeznqEc9F83KU7rnqOOWWX7cN+MUji3sp33EQRUnZcA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1741604280; c=relaxed/simple; bh=TJugW9qRmuA5oYe1TVo0wUzv7Efg6z9e8snaknO8MGs=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=i/g6r6iOCvLOzfrC2gMrKQ+iljugNJ8onBPhI5dnCy9JEWiFTW400zQdlB19bKdVTB+84AV80C1iA3eeWzuY+p/oCrQ8qoDo4fuB7FdVJ/4c1b1h6OoGVirKPgXNEgVjqrtvAzQ77kKC69Th4UAOp3w4QBILQw4CK/ZPiANxV20= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me; spf=pass smtp.mailfrom=pm.me; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b=sLYnl8SX; arc=none smtp.client-ip=185.70.43.22 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pm.me Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b="sLYnl8SX" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pm.me; s=protonmail3; t=1741604274; x=1741863474; bh=2lDf8dq/WG7sVxkTl4G1A2xfuBb4q1lzSGrMrbLyXs8=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector:List-Unsubscribe:List-Unsubscribe-Post; b=sLYnl8SXGBMT9Cl3dFLmQR7LGMUmEQa5bR+arMMPCUqXa+HKhWD4BlLmlQ6Sem1CH 5q50wKLl8Yz+Yy6AYIojHMzUjCRQLTIJ45Jbq5r+u53x5JwQfHvbC2HQfnMrlxiwIo 95+5YpzveROj+LGyuo7yffXv4q1mktBFZi/79bdbU6d5Ipzz25DnjEPLpBey54IoEf HSzu9QW2twy8///rUsFhO1kdpy4NU52XnL3JRjcWpC4Qfo2Rg6uHVIKdakvNrNsskG giex8A0OmZKlCkMdGT00hrPRGfWyAUVNXGTqWhqBm7ZgyKLZ42NjPsgLhT2fzGirCH KRbbese+h7bTQ== Date: Mon, 10 Mar 2025 10:57:47 +0000 To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?Q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Asahi Lina From: Oliver Mangold Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Oliver Mangold Subject: [PATCH v7 4/4] rust: adding OwnableRefCounted and SimpleOwnableRefCounted Message-ID: <20250310-unique-ref-v7-4-4caddb78aa05@pm.me> In-Reply-To: <20250310-unique-ref-v7-0-4caddb78aa05@pm.me> References: <20250310-unique-ref-v7-0-4caddb78aa05@pm.me> Feedback-ID: 31808448:user:proton X-Pm-Message-ID: f81b6abac87746fd99aa4c9bc405baef6d8e08b0 Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Types implementing one of these traits can safely convert between an ARef and an Owned. Signed-off-by: Oliver Mangold --- rust/kernel/types.rs | 275 +++++++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 275 insertions(+) diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index e6f3308f931d90718d405443c3034a216388e0af..3e703701b2bccf1a440f4064b6d= d90afb204d937 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -552,6 +552,12 @@ fn from(b: &T) -> Self { } } =20 +impl From> for ARef { + fn from(b: Owned) -> Self { + T::into_shared(b) + } +} + impl Drop for ARef { fn drop(&mut self) { // SAFETY: The type invariants guarantee that the `ARef` owns the = reference @@ -669,6 +675,275 @@ fn drop(&mut self) { } } =20 +/// 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. +/// - The uniqueness invariant of [`Owned`] is upheld until dropped. +/// - [`try_from_shared()`](OwnableRefCounted::into_shared) only returns a= n [`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()`](OwnableRefCount= ed::into_shared) is left +/// on the default implementation, which just rewraps the underlying obj= ect, 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, +/// } +/// +/// impl Foo { +/// fn new() -> Result, AllocError> { +/// // Use a `KBox` to handle the actual allocation. +/// let result =3D KBox::new( +/// Foo { +/// refcount: Cell::new(1), +/// }, +/// flags::GFP_KERNEL, +/// )?; +/// let result =3D NonNull::new(KBox::into_raw(result)) +/// .expect("Raw pointer to newly allocation KBox is null, thi= s 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 functio= n 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) { +/// // SAFETY: The underlying object is always valid when the func= tion is called. +/// let refcount =3D unsafe { &this.as_ref().refcount }; +/// let new_refcount =3D refcount.get() - 1; +/// if new_refcount =3D=3D 0 { +/// // The `Foo` will be dropped when `KBox` goes out of scope= . +/// // SAFETY: The `Box` is still alive as the old refcou= nt 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) -> Result, ARef> { +/// if this.refcount.get() =3D=3D 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) { +/// // SAFETY: Using `dec_ref()` from `RefCounted` to release is o= kay, as the refcount is +/// // always 1 for an `Owned`. +/// unsafe{ Foo::dec_ref(this) }; +/// } +/// } +/// +/// let foo =3D Foo::new().unwrap(); +/// let mut foo =3D ARef::from(foo); +/// { +/// let bar =3D 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 obje= ct. + fn try_from_shared(this: ARef) -> Result, ARef= >; + /// Converts the [`Owned`] into an [`ARef`]. + + fn into_shared(this: Owned) -> ARef { + // SAFETY: Safe by the requirements 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()`](SimpleOwnableRefCo= unted::inc_ref), +/// [`dec_ref()`](SimpleOwnableRefCounted::dec_ref), +/// and [`is_unique()`](SimpleOwnableRefCounted::is_unique). +/// +/// For non-standard cases where conversion between [`Ownable`] and [`RefC= ounted`] needs +/// or [`Ownable::release()`] and [`RefCounted::dec_ref()`] cannot be the = same method, +/// [`Ownable`], [`RefCounted`] and [`OwnableRefCounted`] should be implem= ented manually. +/// +/// # Safety +/// +/// Implementers must ensure that: +/// +/// - Both the safety requirements as for [`Ownable`] and [`RefCounted`] a= re fulfilled. +/// - [`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 th= an 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, +/// } +/// +/// impl Foo { +/// fn new() -> Result, AllocError> { +/// // Use a KBox to handle the actual allocation. +/// let result =3D KBox::new( +/// Foo { +/// refcount: Cell::new(1), +/// }, +/// flags::GFP_KERNEL, +/// )?; +/// let result =3D NonNull::new(KBox::into_raw(result)) +/// .expect("Raw pointer to newly allocation KBox is null, thi= s 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 SimpleOwnableRefCounted for Foo { +/// fn inc_ref(&self) { +/// self.refcount.replace(self.refcount.get() + 1); +/// } +/// +/// unsafe fn dec_ref(this: NonNull) { +/// // SAFETY: The underlying object is always valid when the func= tion is called. +/// let refcount =3D unsafe { &this.as_ref().refcount }; +/// let new_refcount =3D refcount.get() - 1; +/// if new_refcount =3D=3D 0 { +/// // The `Foo` will be dropped when KBox goes out of scope. +/// // SAFETY: The `Box` is still alive as the old refcou= nt is 1. +/// unsafe { KBox::from_raw(this.as_ptr()) }; +/// } else { +/// refcount.replace(new_refcount); +/// } +/// } +/// +/// fn is_unique(&self) -> bool { +/// self.refcount.get() =3D=3D 1 +/// } +/// } +/// +/// let foo =3D Foo::new().unwrap(); +/// let mut foo =3D ARef::from(foo); +/// { +/// let bar =3D 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 o= bject 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 [`SimpleOwna= bleRefCounted`] 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); +} + +#[cfg_attr(RUSTC_HAS_DO_NOT_RECOMMEND, diagnostic::do_not_recommend)] +// SAFETY: Safe by the requirements on implementation of [`SimpleOwnableRe= fCounted`]. +unsafe impl OwnableRefCounted for T { + fn try_from_shared(this: ARef) -> Result, ARef= > { + if T::is_unique(&*this) { + // SAFETY: Safe by the requirements on implementation of [`Sim= pleOwnable`]. + 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 [`SimpleOwnableRe= fCounted`]. +unsafe impl Ownable for T { + unsafe fn release(this: NonNull) { + // SAFETY: Safe by the requirements on implementation of + // [`SimpleOwnableRefCounted::dec_ref()`]. + unsafe { SimpleOwnableRefCounted::dec_ref(this) }; + } +} + +#[cfg_attr(RUSTC_HAS_DO_NOT_RECOMMEND, diagnostic::do_not_recommend)] +// SAFETY: Safe by the requirements on implementation of [`SimpleOwnableRe= fCounted`]. +unsafe impl RefCounted for T { + fn inc_ref(&self) { + SimpleOwnableRefCounted::inc_ref(self); + } + + unsafe fn dec_ref(this: NonNull) { + // SAFETY: Safe by the requirements on implementation of + // [`SimpleOwnableRefCounted::dec_ref()`]. + unsafe { SimpleOwnableRefCounted::dec_ref(this) }; + } +} + +impl TryFrom> for Owned { + type Error =3D ARef; + /// 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) -> Result, Self::Error> { + T::try_from_shared(b) + } +} + /// A sum type that always holds either a value of type `L` or `R`. /// /// # Examples --=20 2.48.1