From 9b2299af3b92eb5b2c2f87965a5fa24a93e90d06 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 8 Mar 2025 11:05:12 +0000 Subject: rust: pin-init: add `std` and `alloc` support from the user-space version To synchronize the kernel's version of pin-init with the user-space version, introduce support for `std` and `alloc`. While the kernel uses neither, the user-space version has to support both. Thus include the required `#[cfg]`s and additional code. Signed-off-by: Benno Lossin Reviewed-by: Fiona Behrens Tested-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250308110339.2997091-17-benno.lossin@proton.me [ Undo the temporary `--extern force:alloc` since now we have contents for `alloc` here. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/Makefile | 2 +- rust/pin-init/src/__internal.rs | 27 +++++++ rust/pin-init/src/alloc.rs | 158 ++++++++++++++++++++++++++++++++++++++++ rust/pin-init/src/lib.rs | 41 +++++++++-- 4 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 rust/pin-init/src/alloc.rs (limited to 'rust') diff --git a/rust/Makefile b/rust/Makefile index 815fbe05ffc8..e761a8cc3bd5 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -121,7 +121,7 @@ rustdoc-pin_init_internal: $(src)/pin-init/internal/src/lib.rs FORCE rustdoc-pin_init: private rustdoc_host = yes rustdoc-pin_init: private rustc_target_flags = --extern pin_init_internal \ - --extern macros --extern force:alloc --cfg kernel --cfg feature=\"alloc\" + --extern macros --extern alloc --cfg kernel --cfg feature=\"alloc\" rustdoc-pin_init: $(src)/pin-init/src/lib.rs rustdoc-pin_init_internal \ rustdoc-macros FORCE +$(call if_changed,rustdoc) diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index 8a53f55e1bbf..cac293fd4bec 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -189,6 +189,33 @@ impl StackInit { } } +#[test] +fn stack_init_reuse() { + use ::std::{borrow::ToOwned, println, string::String}; + use core::pin::pin; + + #[derive(Debug)] + struct Foo { + a: usize, + b: String, + } + let mut slot: Pin<&mut StackInit> = pin!(StackInit::uninit()); + let value: Result, core::convert::Infallible> = + slot.as_mut().init(crate::init!(Foo { + a: 42, + b: "Hello".to_owned(), + })); + let value = value.unwrap(); + println!("{value:?}"); + let value: Result, core::convert::Infallible> = + slot.as_mut().init(crate::init!(Foo { + a: 24, + b: "world!".to_owned(), + })); + let value = value.unwrap(); + println!("{value:?}"); +} + /// When a value of this type is dropped, it drops a `T`. /// /// Can be forgotten to prevent the drop. diff --git a/rust/pin-init/src/alloc.rs b/rust/pin-init/src/alloc.rs new file mode 100644 index 000000000000..e16baa3b434e --- /dev/null +++ b/rust/pin-init/src/alloc.rs @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#[cfg(all(feature = "alloc", not(feature = "std")))] +use alloc::{boxed::Box, sync::Arc}; +#[cfg(feature = "alloc")] +use core::alloc::AllocError; +use core::{mem::MaybeUninit, pin::Pin}; +#[cfg(feature = "std")] +use std::sync::Arc; + +#[cfg(not(feature = "alloc"))] +type AllocError = core::convert::Infallible; + +use crate::{ + init_from_closure, pin_init_from_closure, InPlaceWrite, Init, PinInit, ZeroableOption, +}; + +pub extern crate alloc; + +// SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee). +// +// In this case we are allowed to use `T: ?Sized`, since all zeros is the `None` variant and there +// is no problem with a VTABLE pointer being null. +unsafe impl ZeroableOption for Box {} + +/// Smart pointer that can initialize memory in-place. +pub trait InPlaceInit: Sized { + /// 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) -> Result, E> + where + E: From; + + /// 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 pin_init(init: impl PinInit) -> Result, AllocError> { + // SAFETY: We delegate to `init` and only change the error type. + let init = unsafe { + pin_init_from_closure(|slot| match init.__pinned_init(slot) { + Ok(()) => Ok(()), + Err(i) => match i {}, + }) + }; + Self::try_pin_init(init) + } + + /// Use the given initializer to in-place initialize a `T`. + fn try_init(init: impl Init) -> Result + where + E: From; + + /// Use the given initializer to in-place initialize a `T`. + fn init(init: impl Init) -> Result { + // SAFETY: We delegate to `init` and only change the error type. + let init = unsafe { + init_from_closure(|slot| match init.__init(slot) { + Ok(()) => Ok(()), + Err(i) => match i {}, + }) + }; + Self::try_init(init) + } +} + +#[cfg(feature = "alloc")] +macro_rules! try_new_uninit { + ($type:ident) => { + $type::try_new_uninit()? + }; +} +#[cfg(all(feature = "std", not(feature = "alloc")))] +macro_rules! try_new_uninit { + ($type:ident) => { + $type::new_uninit() + }; +} + +impl InPlaceInit for Box { + #[inline] + fn try_pin_init(init: impl PinInit) -> Result, E> + where + E: From, + { + try_new_uninit!(Box).write_pin_init(init) + } + + #[inline] + fn try_init(init: impl Init) -> Result + where + E: From, + { + try_new_uninit!(Box).write_init(init) + } +} + +impl InPlaceInit for Arc { + #[inline] + fn try_pin_init(init: impl PinInit) -> Result, E> + where + E: From, + { + let mut this = try_new_uninit!(Arc); + let Some(slot) = Arc::get_mut(&mut this) else { + // SAFETY: the Arc has just been created and has no external references + unsafe { core::hint::unreachable_unchecked() } + }; + let slot = slot.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 and this is the only `Arc` to that data. + Ok(unsafe { Pin::new_unchecked(this.assume_init()) }) + } + + #[inline] + fn try_init(init: impl Init) -> Result + where + E: From, + { + let mut this = try_new_uninit!(Arc); + let Some(slot) = Arc::get_mut(&mut this) else { + // SAFETY: the Arc has just been created and has no external references + unsafe { core::hint::unreachable_unchecked() } + }; + let slot = slot.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() }) + } +} + +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 { 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/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 41bfb35c7a2c..58b77a158c34 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -204,8 +204,16 @@ //! [structurally pinned fields]: //! https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field //! [stack]: crate::stack_pin_init -//! [`Arc`]: https://rust.docs.kernel.org/kernel/sync/struct.Arc.html -//! [`Box`]: https://rust.docs.kernel.org/kernel/alloc/kbox/struct.Box.html +#![cfg_attr( + kernel, + doc = "[`Arc`]: https://rust.docs.kernel.org/kernel/sync/struct.Arc.html" +)] +#![cfg_attr( + kernel, + doc = "[`Box`]: https://rust.docs.kernel.org/kernel/alloc/kbox/struct.Box.html" +)] +#![cfg_attr(not(kernel), doc = "[`Arc`]: alloc::alloc::sync::Arc")] +#![cfg_attr(not(kernel), doc = "[`Box`]: alloc::alloc::boxed::Box")] //! [`impl PinInit`]: PinInit //! [`impl PinInit`]: PinInit //! [`impl Init`]: Init @@ -239,6 +247,11 @@ pub mod __internal; #[doc(hidden)] pub mod macros; +#[cfg(any(feature = "std", feature = "alloc"))] +mod alloc; +#[cfg(any(feature = "std", feature = "alloc"))] +pub use alloc::InPlaceInit; + /// Used to specify the pinning information of the fields of a struct. /// /// This is somewhat similar in purpose as @@ -914,8 +927,16 @@ macro_rules! assert_pinned { /// - `slot` is not partially initialized. /// - while constructing the `T` at `slot` it upholds the pinning invariants of `T`. /// -/// [`Arc`]: https://rust.docs.kernel.org/kernel/sync/struct.Arc.html -/// [`Box`]: https://rust.docs.kernel.org/kernel/alloc/kbox/struct.Box.html +#[cfg_attr( + kernel, + doc = "[`Arc`]: https://rust.docs.kernel.org/kernel/sync/struct.Arc.html" +)] +#[cfg_attr( + kernel, + doc = "[`Box`]: https://rust.docs.kernel.org/kernel/alloc/kbox/struct.Box.html" +)] +#[cfg_attr(not(kernel), doc = "[`Arc`]: alloc::alloc::sync::Arc")] +#[cfg_attr(not(kernel), doc = "[`Box`]: alloc::alloc::boxed::Box")] #[must_use = "An initializer must be used in order to create its value."] pub unsafe trait PinInit: Sized { /// Initializes `slot`. @@ -1005,8 +1026,16 @@ where /// Contrary to its supertype [`PinInit`] the caller is allowed to /// move the pointee after initialization. /// -/// [`Arc`]: https://rust.docs.kernel.org/kernel/sync/struct.Arc.html -/// [`Box`]: https://rust.docs.kernel.org/kernel/alloc/kbox/struct.Box.html +#[cfg_attr( + kernel, + doc = "[`Arc`]: https://rust.docs.kernel.org/kernel/sync/struct.Arc.html" +)] +#[cfg_attr( + kernel, + doc = "[`Box`]: https://rust.docs.kernel.org/kernel/alloc/kbox/struct.Box.html" +)] +#[cfg_attr(not(kernel), doc = "[`Arc`]: alloc::alloc::sync::Arc")] +#[cfg_attr(not(kernel), doc = "[`Box`]: alloc::alloc::boxed::Box")] #[must_use = "An initializer must be used in order to create its value."] pub unsafe trait Init: PinInit { /// Initializes `slot`. -- cgit