From 08f983a55ccf0b015e4788d1a0de0da84e4a7626 Mon Sep 17 00:00:00 2001 From: Alex Mantel Date: Fri, 26 Jul 2024 21:24:42 -0700 Subject: rust: Implement the smart pointer `InPlaceInit` for `Arc` For pinned and unpinned initialization of structs, a trait named `InPlaceInit` exists for uniform access. `Arc` did not implement `InPlaceInit` yet, although the functions already existed. The main reason for that, was that the trait itself returned a `Pin`. The `Arc` implementation of the kernel is already implicitly pinned. To enable `Arc` to implement `InPlaceInit` and to have uniform access, for in-place and pinned in-place initialization, an associated type is introduced for `InPlaceInit`. The new implementation of `InPlaceInit` for `Arc` sets `Arc` as the associated type. Older implementations use an explicit `Pin` as the associated type. The implemented methods for `Arc` are mostly moved from a direct implementation on `Arc`. There should be no user impact. The implementation for `ListArc` is omitted, because it is not merged yet. Link: https://github.com/Rust-for-Linux/linux/issues/1079 Signed-off-by: Alex Mantel Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Link: https://lore.kernel.org/r/20240727042442.682109-1-alexmantel93@mailbox.org [ Removed "Rusts" (Benno). - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/init.rs | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) (limited to 'rust/kernel/init.rs') diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index 495c09ebe3a3..771701805a97 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -213,6 +213,7 @@ use crate::{ alloc::{box_ext::BoxExt, AllocError, Flags}, error::{self, Error}, + sync::Arc, sync::UniqueArc, types::{Opaque, ScopeGuard}, }; @@ -1107,11 +1108,17 @@ unsafe impl PinInit for T { /// Smart pointer that can initialize memory in-place. pub trait InPlaceInit: Sized { + /// Pinned version of `Self`. + /// + /// If a type already implicitly pins its pointee, `Pin` is unnecessary. In this case use + /// `Self`, otherwise just use `Pin`. + type PinnedSelf; + /// Use the given pin-initializer to pin-initialize a `T` inside of a new smart pointer of this /// type. /// /// If `T: !Unpin` it will not be able to move afterwards. - fn try_pin_init(init: impl PinInit, flags: Flags) -> Result, E> + fn try_pin_init(init: impl PinInit, flags: Flags) -> Result where E: From; @@ -1119,7 +1126,7 @@ pub trait InPlaceInit: Sized { /// type. /// /// If `T: !Unpin` it will not be able to move afterwards. - fn pin_init(init: impl PinInit, flags: Flags) -> error::Result> + fn pin_init(init: impl PinInit, flags: Flags) -> error::Result where Error: From, { @@ -1148,9 +1155,31 @@ pub trait InPlaceInit: Sized { } } +impl InPlaceInit for Arc { + type PinnedSelf = Self; + + #[inline] + fn try_pin_init(init: impl PinInit, flags: Flags) -> Result + where + E: From, + { + UniqueArc::try_pin_init(init, flags).map(|u| u.into()) + } + + #[inline] + fn try_init(init: impl Init, flags: Flags) -> Result + where + E: From, + { + UniqueArc::try_init(init, flags).map(|u| u.into()) + } +} + impl InPlaceInit for Box { + type PinnedSelf = Pin; + #[inline] - fn try_pin_init(init: impl PinInit, flags: Flags) -> Result, E> + fn try_pin_init(init: impl PinInit, flags: Flags) -> Result where E: From, { @@ -1179,8 +1208,10 @@ impl InPlaceInit for Box { } impl InPlaceInit for UniqueArc { + type PinnedSelf = Pin; + #[inline] - fn try_pin_init(init: impl PinInit, flags: Flags) -> Result, E> + fn try_pin_init(init: impl PinInit, flags: Flags) -> Result where E: From, { -- cgit v1.2.3-70-g09d2 From 6d1c22d0ace31d096b0dab5318c6a0d3219d6456 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Mon, 19 Aug 2024 11:24:39 +0000 Subject: rust: init: add `write_[pin_]init` functions Sometimes it is necessary to split allocation and initialization into two steps. One such situation is when reusing existing allocations obtained via `Box::drop_contents`. See [1] for an example. In order to support this use case add `write_[pin_]init` functions to the pin-init API. These functions operate on already allocated smart pointers that wrap `MaybeUninit`. Link: https://lore.kernel.org/rust-for-linux/f026532f-8594-4f18-9aa5-57ad3f5bc592@proton.me/ [1] Signed-off-by: Benno Lossin Reviewed-by: Boqun Feng Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Link: https://lore.kernel.org/r/20240819112415.99810-2-benno.lossin@proton.me Signed-off-by: Miguel Ojeda --- rust/kernel/init.rs | 84 +++++++++++++++++++++++++++++++++++--------------- rust/kernel/prelude.rs | 2 +- 2 files changed, 61 insertions(+), 25 deletions(-) (limited to 'rust/kernel/init.rs') diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index 771701805a97..a8068f99fcaa 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -1183,13 +1183,7 @@ impl InPlaceInit for Box { where E: From, { - let mut this = as BoxExt<_>>::new_uninit(flags)?; - let slot = this.as_mut_ptr(); - // SAFETY: When init errors/panics, slot will get deallocated but not dropped, - // slot is valid and will not be moved, because we pin it later. - unsafe { init.__pinned_init(slot)? }; - // SAFETY: All fields have been initialized. - Ok(unsafe { this.assume_init() }.into()) + as BoxExt<_>>::new_uninit(flags)?.write_pin_init(init) } #[inline] @@ -1197,13 +1191,7 @@ impl InPlaceInit for Box { where E: From, { - let mut this = as BoxExt<_>>::new_uninit(flags)?; - let slot = this.as_mut_ptr(); - // SAFETY: When init errors/panics, slot will get deallocated but not dropped, - // slot is valid. - unsafe { init.__init(slot)? }; - // SAFETY: All fields have been initialized. - Ok(unsafe { this.assume_init() }) + as BoxExt<_>>::new_uninit(flags)?.write_init(init) } } @@ -1215,13 +1203,7 @@ impl InPlaceInit for UniqueArc { where E: From, { - let mut this = UniqueArc::new_uninit(flags)?; - let slot = this.as_mut_ptr(); - // SAFETY: When init errors/panics, slot will get deallocated but not dropped, - // slot is valid and will not be moved, because we pin it later. - unsafe { init.__pinned_init(slot)? }; - // SAFETY: All fields have been initialized. - Ok(unsafe { this.assume_init() }.into()) + UniqueArc::new_uninit(flags)?.write_pin_init(init) } #[inline] @@ -1229,13 +1211,67 @@ impl InPlaceInit for UniqueArc { where E: From, { - let mut this = UniqueArc::new_uninit(flags)?; - let slot = this.as_mut_ptr(); + UniqueArc::new_uninit(flags)?.write_init(init) + } +} + +/// Smart pointer containing uninitialized memory and that can write a value. +pub trait InPlaceWrite { + /// The type `Self` turns into when the contents are initialized. + type Initialized; + + /// Use the given initializer to write a value into `self`. + /// + /// Does not drop the current value and considers it as uninitialized memory. + fn write_init(self, init: impl Init) -> Result; + + /// Use the given pin-initializer to write a value into `self`. + /// + /// Does not drop the current value and considers it as uninitialized memory. + fn write_pin_init(self, init: impl PinInit) -> Result, E>; +} + +impl InPlaceWrite for Box> { + type Initialized = Box; + + fn write_init(mut self, init: impl Init) -> Result { + let slot = self.as_mut_ptr(); // SAFETY: When init errors/panics, slot will get deallocated but not dropped, // slot is valid. unsafe { init.__init(slot)? }; // SAFETY: All fields have been initialized. - Ok(unsafe { this.assume_init() }) + Ok(unsafe { self.assume_init() }) + } + + fn write_pin_init(mut self, init: impl PinInit) -> Result, E> { + let slot = self.as_mut_ptr(); + // SAFETY: When init errors/panics, slot will get deallocated but not dropped, + // slot is valid and will not be moved, because we pin it later. + unsafe { init.__pinned_init(slot)? }; + // SAFETY: All fields have been initialized. + Ok(unsafe { self.assume_init() }.into()) + } +} + +impl InPlaceWrite for UniqueArc> { + type Initialized = UniqueArc; + + fn write_init(mut self, init: impl Init) -> Result { + let slot = self.as_mut_ptr(); + // SAFETY: When init errors/panics, slot will get deallocated but not dropped, + // slot is valid. + unsafe { init.__init(slot)? }; + // SAFETY: All fields have been initialized. + Ok(unsafe { self.assume_init() }) + } + + fn write_pin_init(mut self, init: impl PinInit) -> Result, E> { + let slot = self.as_mut_ptr(); + // SAFETY: When init errors/panics, slot will get deallocated but not dropped, + // slot is valid and will not be moved, because we pin it later. + unsafe { init.__pinned_init(slot)? }; + // SAFETY: All fields have been initialized. + Ok(unsafe { self.assume_init() }.into()) } } diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index b37a0b3180fb..4571daec0961 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -37,6 +37,6 @@ pub use super::error::{code::*, Error, Result}; pub use super::{str::CStr, ThisModule}; -pub use super::init::{InPlaceInit, Init, PinInit}; +pub use super::init::{InPlaceInit, InPlaceWrite, Init, PinInit}; pub use super::current; -- cgit v1.2.3-70-g09d2 From 0528ca0a4f858da3369d405af8c76b8248dfeb7b Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Wed, 14 Aug 2024 08:05:20 +0000 Subject: rust: init: add `assert_pinned` macro Add a macro to statically check if a field of a struct is marked with `#[pin]` ie that it is structurally pinned. This can be used when `unsafe` code needs to rely on fields being structurally pinned. The macro has a special "inline" mode for the case where the type depends on generic parameters from the surrounding scope. Signed-off-by: Benno Lossin Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl Link: https://lore.kernel.org/r/20240814-linked-list-v5-1-f5f5e8075da0@google.com [ Replaced `compile_fail` with `ignore` and a TODO note. Removed `pub` from example to clean `unreachable_pub` lint. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/init.rs | 68 ++++++++++++++++++++++++++++++++++++++++++ rust/kernel/init/__internal.rs | 29 ++++++++++++++++++ 2 files changed, 97 insertions(+) (limited to 'rust/kernel/init.rs') diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index a8068f99fcaa..a17ac8762d8f 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -743,6 +743,74 @@ macro_rules! try_init { }; } +/// Asserts that a field on a struct using `#[pin_data]` is marked with `#[pin]` ie. that it is +/// structurally pinned. +/// +/// # Example +/// +/// This will succeed: +/// ``` +/// use kernel::assert_pinned; +/// #[pin_data] +/// struct MyStruct { +/// #[pin] +/// some_field: u64, +/// } +/// +/// assert_pinned!(MyStruct, some_field, u64); +/// ``` +/// +/// This will fail: +// TODO: replace with `compile_fail` when supported. +/// ```ignore +/// use kernel::assert_pinned; +/// #[pin_data] +/// struct MyStruct { +/// some_field: u64, +/// } +/// +/// assert_pinned!(MyStruct, some_field, u64); +/// ``` +/// +/// Some uses of the macro may trigger the `can't use generic parameters from outer item` error. To +/// work around this, you may pass the `inline` parameter to the macro. The `inline` parameter can +/// only be used when the macro is invoked from a function body. +/// ``` +/// use kernel::assert_pinned; +/// #[pin_data] +/// struct Foo { +/// #[pin] +/// elem: T, +/// } +/// +/// impl Foo { +/// fn project(self: Pin<&mut Self>) -> Pin<&mut T> { +/// assert_pinned!(Foo, elem, T, inline); +/// +/// // SAFETY: The field is structurally pinned. +/// unsafe { self.map_unchecked_mut(|me| &mut me.elem) } +/// } +/// } +/// ``` +#[macro_export] +macro_rules! assert_pinned { + ($ty:ty, $field:ident, $field_ty:ty, inline) => { + let _ = move |ptr: *mut $field_ty| { + // SAFETY: This code is unreachable. + let data = unsafe { <$ty as $crate::init::__internal::HasPinData>::__pin_data() }; + let init = $crate::init::__internal::AlwaysFail::<$field_ty>::new(); + // SAFETY: This code is unreachable. + unsafe { data.$field(ptr, init) }.ok(); + }; + }; + + ($ty:ty, $field:ident, $field_ty:ty) => { + const _: () = { + $crate::assert_pinned!($ty, $field, $field_ty, inline); + }; + }; +} + /// A pin-initializer for the type `T`. /// /// To use this initializer, you will need a suitable memory location that can hold a `T`. This can diff --git a/rust/kernel/init/__internal.rs b/rust/kernel/init/__internal.rs index db3372619ecd..13cefd37512f 100644 --- a/rust/kernel/init/__internal.rs +++ b/rust/kernel/init/__internal.rs @@ -228,3 +228,32 @@ impl OnlyCallFromDrop { Self(()) } } + +/// Initializer that always fails. +/// +/// Used by [`assert_pinned!`]. +/// +/// [`assert_pinned!`]: crate::assert_pinned +pub struct AlwaysFail { + _t: PhantomData, +} + +impl AlwaysFail { + /// Creates a new initializer that always fails. + pub fn new() -> Self { + Self { _t: PhantomData } + } +} + +impl Default for AlwaysFail { + fn default() -> Self { + Self::new() + } +} + +// SAFETY: `__pinned_init` always fails, which is always okay. +unsafe impl PinInit for AlwaysFail { + unsafe fn __pinned_init(self, _slot: *mut T) -> Result<(), ()> { + Err(()) + } +} -- cgit v1.2.3-70-g09d2