From 9b2299af3b92eb5b2c2f87965a5fa24a93e90d06 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 8 Mar 2025 11:05:12 +0000 Subject: [PATCH 01/16] 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 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`. -- 2.51.0 From 02c01c089d125ccc1ecbf331481e7de6f1f38f4e Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 8 Mar 2025 11:05:17 +0000 Subject: [PATCH 02/16] rust: pin-init: synchronize documentation with the user-space version Synchronize documentation and examples with the user-space version. Signed-off-by: Benno Lossin Reviewed-by: Fiona Behrens Reviewed-by: Andreas Hindborg Tested-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250308110339.2997091-18-benno.lossin@proton.me Signed-off-by: Miguel Ojeda --- rust/pin-init/src/__internal.rs | 8 +- rust/pin-init/src/lib.rs | 141 +++++++++++++++++++++++--------- rust/pin-init/src/macros.rs | 20 ++--- 3 files changed, 115 insertions(+), 54 deletions(-) diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index cac293fd4bec..7f7744d48575 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -1,11 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -//! This module contains API-internal items for pin-init. +//! This module contains library internal items. //! -//! These items must not be used outside of -//! - `kernel/init.rs` -//! - `macros/pin_data.rs` -//! - `macros/pinned_drop.rs` +//! These items must not be used outside of this crate and the pin-init-internal crate located at +//! `../internal`. use super::*; diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 58b77a158c34..a00288133ae3 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1,10 +1,37 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT -//! API to safely and fallibly initialize pinned `struct`s using in-place constructors. +//! Library to safely and fallibly initialize pinned `struct`s using in-place constructors. +//! +//! [Pinning][pinning] is Rust's way of ensuring data does not move. //! //! It also allows in-place initialization of big `struct`s that would otherwise produce a stack //! overflow. //! +//! This library's main use-case is in [Rust-for-Linux]. Although this version can be used +//! standalone. +//! +//! There are cases when you want to in-place initialize a struct. For example when it is very big +//! and moving it from the stack is not an option, because it is bigger than the stack itself. +//! Another reason would be that you need the address of the object to initialize it. This stands +//! in direct conflict with Rust's normal process of first initializing an object and then moving +//! it into it's final memory location. For more information, see +//! . +//! +//! This library allows you to do in-place initialization safely. +//! +//! ## Nightly Needed for `alloc` feature +//! +//! This library requires the [`allocator_api` unstable feature] when the `alloc` feature is +//! enabled and thus this feature can only be used with a nightly compiler. When enabling the +//! `alloc` feature, the user will be required to activate `allocator_api` as well. +//! +//! [`allocator_api` unstable feature]: https://doc.rust-lang.org/nightly/unstable-book/library-features/allocator-api.html +//! +//! The feature is enabled by default, thus by default `pin-init` will require a nightly compiler. +//! However, using the crate on stable compilers is possible by disabling `alloc`. In practice this +//! will require the `std` feature, because stable compilers have neither `Box` nor `Arc` in no-std +//! mode. +//! //! # Overview //! //! To initialize a `struct` with an in-place constructor you will need two things: @@ -17,12 +44,17 @@ //! - a custom function/macro returning an in-place constructor provided by someone else, //! - using the unsafe function [`pin_init_from_closure()`] to manually create an initializer. //! -//! Aside from pinned initialization, this API also supports in-place construction without pinning, -//! the macros/types/functions are generally named like the pinned variants without the `pin` -//! prefix. +//! Aside from pinned initialization, this library also supports in-place construction without +//! pinning, the macros/types/functions are generally named like the pinned variants without the +//! `pin_` prefix. //! //! # Examples //! +//! Throughout the examples we will often make use of the `CMutex` type which can be found in +//! `../examples/mutex.rs`. It is essentially a userland rebuild of the `struct mutex` type from +//! the Linux kernel. It also uses a wait list and a basic spinlock. Importantly the wait list +//! requires it to be pinned to be locked and thus is a prime candidate for using this library. +//! //! ## Using the [`pin_init!`] macro //! //! If you want to use [`PinInit`], then you will have to annotate your `struct` with @@ -36,7 +68,7 @@ //! # #![feature(allocator_api)] //! # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; //! # use core::pin::Pin; -//! use pin_init::*; +//! use pin_init::{pin_data, pin_init, InPlaceInit}; //! //! #[pin_data] //! struct Foo { @@ -80,8 +112,8 @@ //! //! ## Using a custom function/macro that returns an initializer //! -//! Many types from the kernel supply a function/macro that returns an initializer, because the -//! above method only works for types where you can access the fields. +//! Many types that use this library supply a function/macro that returns an initializer, because +//! the above method only works for types where you can access the fields. //! //! ```rust,ignore //! # #![feature(allocator_api)] @@ -132,7 +164,7 @@ //! //! ```rust,ignore //! # #![feature(extern_types)] -//! use pin_init::*; +//! use pin_init::{pin_data, pinned_drop, PinInit, PinnedDrop, pin_init_from_closure}; //! use core::{ //! ptr::addr_of_mut, //! marker::PhantomPinned, @@ -141,8 +173,11 @@ //! mem::MaybeUninit, //! }; //! mod bindings { +//! #[repr(C)] +//! pub struct foo { +//! /* fields from C ... */ +//! } //! extern "C" { -//! pub type foo; //! pub fn init_foo(ptr: *mut foo); //! pub fn destroy_foo(ptr: *mut foo); //! #[must_use = "you must check the error return code"] @@ -200,6 +235,10 @@ //! } //! ``` //! +//! For more information on how to use [`pin_init_from_closure()`], take a look at the uses inside +//! the `kernel` crate. The [`sync`] module is a good starting point. +//! +//! [`sync`]: https://rust.docs.kernel.org/kernel/sync/index.html //! [pinning]: https://doc.rust-lang.org/std/pin/index.html //! [structurally pinned fields]: //! https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field @@ -214,11 +253,10 @@ )] #![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 -//! [`pin_data`]: crate::pin_data -//! [`pin_init!`]: crate::pin_init! +//! [`impl PinInit`]: crate::PinInit +//! [`impl PinInit`]: crate::PinInit +//! [`impl Init`]: crate::Init +//! [Rust-for-Linux]: https://rust-for-linux.com/ #![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))] #![cfg_attr( @@ -404,8 +442,6 @@ pub use ::pin_init_internal::Zeroable; /// A normal `let` binding with optional type annotation. The expression is expected to implement /// [`PinInit`]/[`Init`] with the error type [`Infallible`]. If you want to use a different error /// type, then use [`stack_try_pin_init!`]. -/// -/// [`stack_try_pin_init!`]: crate::stack_try_pin_init! #[macro_export] macro_rules! stack_pin_init { (let $var:ident $(: $t:ty)? = $val:expr) => { @@ -542,10 +578,10 @@ macro_rules! stack_try_pin_init { /// /// # Init-functions /// -/// When working with this API it is often desired to let others construct your types without -/// giving access to all fields. This is where you would normally write a plain function `new` -/// that would return a new instance of your type. With this API that is also possible. -/// However, there are a few extra things to keep in mind. +/// When working with this library it is often desired to let others construct your types without +/// giving access to all fields. This is where you would normally write a plain function `new` that +/// would return a new instance of your type. With this library that is also possible. However, +/// there are a few extra things to keep in mind. /// /// To create an initializer function, simply declare it like this: /// @@ -674,22 +710,22 @@ macro_rules! stack_try_pin_init { /// #[pin] /// pin: PhantomPinned, /// } -/// pin_init!(&this in Buf { +/// +/// let init = pin_init!(&this in Buf { /// buf: [0; 64], /// // SAFETY: TODO. /// ptr: unsafe { addr_of_mut!((*this.as_ptr()).buf).cast() }, /// pin: PhantomPinned, /// }); -/// pin_init!(Buf { +/// let init = pin_init!(Buf { /// buf: [1; 64], /// ..Zeroable::zeroed() /// }); /// ``` /// -/// [`try_pin_init!`]: crate::try_pin_init /// [`NonNull`]: core::ptr::NonNull // For a detailed example of how this macro works, see the module documentation of the hidden -// module `__internal` inside of `init/__internal.rs`. +// module `macros` inside of `macros.rs`. #[macro_export] macro_rules! pin_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { @@ -719,7 +755,8 @@ macro_rules! pin_init { /// ```rust,ignore /// # #![feature(allocator_api)] /// # #[path = "../examples/error.rs"] mod error; use error::Error; -/// use pin_init::*; +/// use pin_init::{pin_data, try_pin_init, PinInit, InPlaceInit, zeroed}; +/// /// #[pin_data] /// struct BigBuf { /// big: Box<[u8; 1024 * 1024 * 1024]>, @@ -730,7 +767,7 @@ macro_rules! pin_init { /// impl BigBuf { /// fn new() -> impl PinInit { /// try_pin_init!(Self { -/// big: Box::init(init::zeroed())?, +/// big: Box::init(zeroed())?, /// small: [0; 1024 * 1024], /// ptr: core::ptr::null_mut(), /// }? Error) @@ -739,7 +776,7 @@ macro_rules! pin_init { /// # let _ = Box::pin_init(BigBuf::new()); /// ``` // For a detailed example of how this macro works, see the module documentation of the hidden -// module `__internal` inside of `init/__internal.rs`. +// module `macros` inside of `macros.rs`. #[macro_export] macro_rules! try_pin_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { @@ -772,9 +809,30 @@ macro_rules! try_pin_init { /// This initializer is for initializing data in-place that might later be moved. If you want to /// pin-initialize, use [`pin_init!`]. /// -/// [`try_init!`]: crate::try_init! +/// # Examples +/// +/// ```rust +/// # #![feature(allocator_api)] +/// # #[path = "../examples/error.rs"] mod error; use error::Error; +/// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; +/// # use pin_init::InPlaceInit; +/// use pin_init::{init, Init, zeroed}; +/// +/// struct BigBuf { +/// small: [u8; 1024 * 1024], +/// } +/// +/// impl BigBuf { +/// fn new() -> impl Init { +/// init!(Self { +/// small <- zeroed(), +/// }) +/// } +/// } +/// # let _ = Box::init(BigBuf::new()); +/// ``` // For a detailed example of how this macro works, see the module documentation of the hidden -// module `__internal` inside of `init/__internal.rs`. +// module `macros` inside of `macros.rs`. #[macro_export] macro_rules! init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { @@ -804,7 +862,9 @@ macro_rules! init { /// ```rust,ignore /// # #![feature(allocator_api)] /// # use core::alloc::AllocError; -/// use pin_init::*; +/// # use pin_init::InPlaceInit; +/// use pin_init::{try_init, Init, zeroed}; +/// /// struct BigBuf { /// big: Box<[u8; 1024 * 1024 * 1024]>, /// small: [u8; 1024 * 1024], @@ -818,10 +878,10 @@ macro_rules! init { /// }? AllocError) /// } /// } +/// # let _ = Box::init(BigBuf::new()); /// ``` -/// [`try_pin_init!`]: crate::try_pin_init // For a detailed example of how this macro works, see the module documentation of the hidden -// module `__internal` inside of `init/__internal.rs`. +// module `macros` inside of `macros.rs`. #[macro_export] macro_rules! try_init { ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? { @@ -847,7 +907,8 @@ macro_rules! try_init { /// /// This will succeed: /// ```ignore -/// use pin_init::assert_pinned; +/// use pin_init::{pin_data, assert_pinned}; +/// /// #[pin_data] /// struct MyStruct { /// #[pin] @@ -859,7 +920,8 @@ macro_rules! try_init { /// /// This will fail: /// ```compile_fail,ignore -/// use pin_init::assert_pinned; +/// use pin_init::{pin_data, assert_pinned}; +/// /// #[pin_data] /// struct MyStruct { /// some_field: u64, @@ -872,7 +934,9 @@ macro_rules! try_init { /// 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. /// ```ignore -/// use pin_init::assert_pinned; +/// # use core::pin::Pin; +/// use pin_init::{pin_data, assert_pinned}; +/// /// #[pin_data] /// struct Foo { /// #[pin] @@ -1056,14 +1120,15 @@ pub unsafe trait Init: PinInit { /// /// ```rust,ignore /// # #![expect(clippy::disallowed_names)] - /// use pin_init::{init_from_closure, zeroed}; + /// use pin_init::{init, zeroed, Init}; + /// /// struct Foo { /// buf: [u8; 1_000_000], /// } /// /// impl Foo { /// fn setup(&mut self) { - /// pr_info!("Setting up foo"); + /// println!("Setting up foo"); /// } /// } /// @@ -1302,8 +1367,6 @@ pub trait InPlaceWrite { /// # Safety /// /// This trait must be implemented via the [`pinned_drop`] proc-macro attribute on the impl. -/// -/// [`pinned_drop`]: crate::pinned_drop pub unsafe trait PinnedDrop: __internal::HasPinData { /// Executes the pinned destructor of this type. /// diff --git a/rust/pin-init/src/macros.rs b/rust/pin-init/src/macros.rs index d41c4f198c42..361623324d5c 100644 --- a/rust/pin-init/src/macros.rs +++ b/rust/pin-init/src/macros.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! This module provides the macros that actually implement the proc-macros `pin_data` and -//! `pinned_drop`. It also contains `__init_internal` the implementation of the `{try_}{pin_}init!` -//! macros. +//! `pinned_drop`. It also contains `__init_internal`, the implementation of the +//! `{try_}{pin_}init!` macros. //! //! These macros should never be called directly, since they expect their input to be //! in a certain format which is internal. If used incorrectly, these macros can lead to UB even in @@ -11,16 +11,17 @@ //! This architecture has been chosen because the kernel does not yet have access to `syn` which //! would make matters a lot easier for implementing these as proc-macros. //! +//! Since this library and the kernel implementation should diverge as little as possible, the same +//! approach has been taken here. +//! //! # Macro expansion example //! //! This section is intended for readers trying to understand the macros in this module and the -//! `pin_init!` macros from `init.rs`. +//! `[try_][pin_]init!` macros from `lib.rs`. //! //! We will look at the following example: //! //! ```rust,ignore -//! # use pin_init::*; -//! # use core::pin::Pin; //! #[pin_data] //! #[repr(C)] //! struct Bar { @@ -45,7 +46,7 @@ //! #[pinned_drop] //! impl PinnedDrop for Foo { //! fn drop(self: Pin<&mut Self>) { -//! pr_info!("{self:p} is getting dropped."); +//! println!("{self:p} is getting dropped."); //! } //! } //! @@ -75,7 +76,6 @@ //! Here is the definition of `Bar` from our example: //! //! ```rust,ignore -//! # use pin_init::*; //! #[pin_data] //! #[repr(C)] //! struct Bar { @@ -251,7 +251,7 @@ //! // is an error later. This `DropGuard` will drop the field when it gets //! // dropped and has not yet been forgotten. //! let __t_guard = unsafe { -//! ::pinned_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).t)) +//! ::pin_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).t)) //! }; //! // Expansion of `x: 0,`: //! // Since this can be an arbitrary expression we cannot place it inside @@ -412,7 +412,7 @@ //! #[pinned_drop] //! impl PinnedDrop for Foo { //! fn drop(self: Pin<&mut Self>) { -//! pr_info!("{self:p} is getting dropped."); +//! println!("{self:p} is getting dropped."); //! } //! } //! ``` @@ -423,7 +423,7 @@ //! // `unsafe`, full path and the token parameter are added, everything else stays the same. //! unsafe impl ::pin_init::PinnedDrop for Foo { //! fn drop(self: Pin<&mut Self>, _: ::pin_init::__internal::OnlyCallFromDrop) { -//! pr_info!("{self:p} is getting dropped."); +//! println!("{self:p} is getting dropped."); //! } //! } //! ``` -- 2.51.0 From 7cb5dee4c8349f8cc3e1ce529df4e18ebe3fed2e Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 8 Mar 2025 11:05:22 +0000 Subject: [PATCH 03/16] rust: pin-init: internal: synchronize with user-space version Synchronize the internal macros crate with the user-space version that uses the quote crate [1] instead of a custom `quote!` macro. The imports in the different version are achieved using `cfg` on the kernel config value. This cfg is always set in the kernel and never set in the user-space version. Since the quote crate requires the proc_macro2 crate, imports also need to be adjusted and `.into()` calls have to be inserted. Link: https://crates.io/crates/quote [1] Signed-off-by: Benno Lossin Reviewed-by: Andreas Hindborg Tested-by: Andreas Hindborg Reviewed-by: Fiona Behrens Link: https://lore.kernel.org/r/20250308110339.2997091-19-benno.lossin@proton.me Signed-off-by: Miguel Ojeda --- rust/pin-init/internal/src/helpers.rs | 3 +++ rust/pin-init/internal/src/lib.rs | 16 +++++++++++++--- rust/pin-init/internal/src/pin_data.rs | 3 +++ rust/pin-init/internal/src/pinned_drop.rs | 3 +++ rust/pin-init/internal/src/zeroable.rs | 3 +++ 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/rust/pin-init/internal/src/helpers.rs b/rust/pin-init/internal/src/helpers.rs index 78521ba19d0b..236f989a50f2 100644 --- a/rust/pin-init/internal/src/helpers.rs +++ b/rust/pin-init/internal/src/helpers.rs @@ -1,5 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT +#[cfg(not(kernel))] +use proc_macro2 as proc_macro; + use proc_macro::{TokenStream, TokenTree}; /// Parsed generics. diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs index c201b8a53915..30e145f80bc0 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -7,6 +7,13 @@ //! `pin-init` proc macros. #![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))] +// Allow `.into()` to convert +// - `proc_macro2::TokenStream` into `proc_macro::TokenStream` in the user-space version. +// - `proc_macro::TokenStream` into `proc_macro::TokenStream` in the kernel version. +// Clippy warns on this conversion, but it's required by the user-space version. +// +// Remove once we have `proc_macro2` in the kernel. +#![allow(clippy::useless_conversion)] use proc_macro::TokenStream; @@ -14,6 +21,9 @@ use proc_macro::TokenStream; #[path = "../../../macros/quote.rs"] #[macro_use] mod quote; +#[cfg(not(kernel))] +#[macro_use] +extern crate quote; mod helpers; mod pin_data; @@ -23,17 +33,17 @@ mod zeroable; #[allow(missing_docs)] #[proc_macro_attribute] pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream { - pin_data::pin_data(inner, item) + pin_data::pin_data(inner.into(), item.into()).into() } #[allow(missing_docs)] #[proc_macro_attribute] pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { - pinned_drop::pinned_drop(args, input) + pinned_drop::pinned_drop(args.into(), input.into()).into() } #[allow(missing_docs)] #[proc_macro_derive(Zeroable)] pub fn derive_zeroable(input: TokenStream) -> TokenStream { - zeroable::derive(input) + zeroable::derive(input.into()).into() } diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 9b974498f4a8..87d4a7eb1d35 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -1,5 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT +#[cfg(not(kernel))] +use proc_macro2 as proc_macro; + use crate::helpers::{parse_generics, Generics}; use proc_macro::{Group, Punct, Spacing, TokenStream, TokenTree}; diff --git a/rust/pin-init/internal/src/pinned_drop.rs b/rust/pin-init/internal/src/pinned_drop.rs index 386f52f73c06..c824dd8b436d 100644 --- a/rust/pin-init/internal/src/pinned_drop.rs +++ b/rust/pin-init/internal/src/pinned_drop.rs @@ -1,5 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT +#[cfg(not(kernel))] +use proc_macro2 as proc_macro; + use proc_macro::{TokenStream, TokenTree}; pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream { diff --git a/rust/pin-init/internal/src/zeroable.rs b/rust/pin-init/internal/src/zeroable.rs index 0cf6732f27dc..acc94008c152 100644 --- a/rust/pin-init/internal/src/zeroable.rs +++ b/rust/pin-init/internal/src/zeroable.rs @@ -1,5 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 +#[cfg(not(kernel))] +use proc_macro2 as proc_macro; + use crate::helpers::{parse_generics, Generics}; use proc_macro::{TokenStream, TokenTree}; -- 2.51.0 From a9fa3a9c6e28658cc6018a06310a9327add606ab Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 8 Mar 2025 11:05:27 +0000 Subject: [PATCH 04/16] rust: pin-init: miscellaneous synchronization with the user-space version Remove the last differences between the kernel version and the user-space version. Signed-off-by: Benno Lossin Reviewed-by: Andreas Hindborg Tested-by: Andreas Hindborg Reviewed-by: Fiona Behrens Link: https://lore.kernel.org/r/20250308110339.2997091-20-benno.lossin@proton.me Signed-off-by: Miguel Ojeda --- rust/pin-init/internal/src/lib.rs | 5 ++--- rust/pin-init/src/__internal.rs | 2 +- rust/pin-init/src/lib.rs | 2 -- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/rust/pin-init/internal/src/lib.rs b/rust/pin-init/internal/src/lib.rs index 30e145f80bc0..babe5e878550 100644 --- a/rust/pin-init/internal/src/lib.rs +++ b/rust/pin-init/internal/src/lib.rs @@ -14,6 +14,8 @@ // // Remove once we have `proc_macro2` in the kernel. #![allow(clippy::useless_conversion)] +// Documentation is done in the pin-init crate instead. +#![allow(missing_docs)] use proc_macro::TokenStream; @@ -30,19 +32,16 @@ mod pin_data; mod pinned_drop; mod zeroable; -#[allow(missing_docs)] #[proc_macro_attribute] pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream { pin_data::pin_data(inner.into(), item.into()).into() } -#[allow(missing_docs)] #[proc_macro_attribute] pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { pinned_drop::pinned_drop(args.into(), input.into()).into() } -#[allow(missing_docs)] #[proc_macro_derive(Zeroable)] pub fn derive_zeroable(input: TokenStream) -> TokenStream { zeroable::derive(input.into()).into() diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index 7f7744d48575..557b5948cddc 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -14,7 +14,7 @@ use super::*; /// /// [nomicon]: https://doc.rust-lang.org/nomicon/subtyping.html /// [this table]: https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns -pub(super) type Invariant = PhantomData *mut T>; +pub(crate) type Invariant = PhantomData *mut T>; /// Module-internal type implementing `PinInit` and `Init`. /// diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index a00288133ae3..45880ffa81bb 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -349,8 +349,6 @@ pub use alloc::InPlaceInit; /// } /// } /// ``` -/// -/// [`pin_init!`]: crate::pin_init pub use ::pin_init_internal::pin_data; /// Used to implement `PinnedDrop` safely. -- 2.51.0 From 2e5f4f3cf27b620ebf0f403fd0dfe680c437600b Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 8 Mar 2025 11:05:31 +0000 Subject: [PATCH 05/16] rust: pin-init: add miscellaneous files from the user-space version Add readme and contribution guidelines of the user-space version of pin-init. Signed-off-by: Benno Lossin Reviewed-by: Fiona Behrens Reviewed-by: Andreas Hindborg Tested-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250308110339.2997091-21-benno.lossin@proton.me Signed-off-by: Miguel Ojeda --- rust/pin-init/CONTRIBUTING.md | 72 +++++++++++ rust/pin-init/README.md | 228 ++++++++++++++++++++++++++++++++++ 2 files changed, 300 insertions(+) create mode 100644 rust/pin-init/CONTRIBUTING.md create mode 100644 rust/pin-init/README.md diff --git a/rust/pin-init/CONTRIBUTING.md b/rust/pin-init/CONTRIBUTING.md new file mode 100644 index 000000000000..16c899a7ae0b --- /dev/null +++ b/rust/pin-init/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributing to `pin-init` + +Thanks for showing interest in contributing to `pin-init`! This document outlines the guidelines for +contributing to `pin-init`. + +All contributions are double-licensed under Apache 2.0 and MIT. You can find the respective licenses +in the `LICENSE-APACHE` and `LICENSE-MIT` files. + +## Non-Code Contributions + +### Bug Reports + +For any type of bug report, please submit an issue using the bug report issue template. + +If the issue is a soundness issue, please privately report it as a security vulnerability via the +GitHub web interface. + +### Feature Requests + +If you have any feature requests, please submit an issue using the feature request issue template. + +### Questions and Getting Help + +You can ask questions in the Discussions page of the GitHub repository. If you're encountering +problems or just have questions related to `pin-init` in the Linux kernel, you can also ask your +questions in the [Rust-for-Linux Zulip](https://rust-for-linux.zulipchat.com/) or see +. + +## Contributing Code + +### Linux Kernel + +`pin-init` is used by the Linux kernel and all commits are synchronized to it. For this reason, the +same requirements for commits apply to `pin-init`. See [the kernel's documentation] for details. The +rest of this document will also cover some of the rules listed there and additional ones. + +[the kernel's documentation]: https://docs.kernel.org/process/submitting-patches.html + +Contributions to `pin-init` ideally go through the [GitHub repository], because that repository runs +a CI with lots of tests not present in the kernel. However, patches are also accepted (though not +preferred). Do note that there are some files that are only present in the GitHub repository such as +tests, licenses and cargo related files. Making changes to them can only happen via GitHub. + +[GitHub repository]: https://github.com/Rust-for-Linux/pin-init + +### Commit Style + +Everything must compile without errors or warnings and all tests must pass after **every commit**. +This is important for bisection and also required by the kernel. + +Each commit should be a single, logically cohesive change. Of course it's best to keep the changes +small and digestible, but logically linked changes should be made in the same commit. For example, +when fixing typos, create a single commit that fixes all of them instead of one commit per typo. + +Commits must have a meaningful commit title. Commits with changes to files in the `internal` +directory should have a title prefixed with `internal:`. The commit message should explain the +change and its rationale. You also have to add your `Signed-off-by` tag, see [Developer's +Certificate of Origin]. This has to be done for both mailing list submissions as well as GitHub +submissions. + +[Developer's Certificate of Origin]: https://docs.kernel.org/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin + +Any changes made to public APIs must be documented not only in the commit message, but also in the +`CHANGELOG.md` file. This is especially important for breaking changes, as those warrant a major +version bump. + +If you make changes to the top-level crate documentation, you also need to update the `README.md` +via `cargo rdme`. + +Some of these rules can be ignored if the change is done solely to files that are not present in the +kernel version of this library. Those files are documented in the `sync-kernel.sh` script at the +very bottom in the `--exclude` flag given to the `git am` command. diff --git a/rust/pin-init/README.md b/rust/pin-init/README.md new file mode 100644 index 000000000000..3d04796b212b --- /dev/null +++ b/rust/pin-init/README.md @@ -0,0 +1,228 @@ +[![Crates.io](https://img.shields.io/crates/v/pin-init.svg)](https://crates.io/crates/pin-init) +[![Documentation](https://docs.rs/pin-init/badge.svg)](https://docs.rs/pin-init/) +[![Dependency status](https://deps.rs/repo/github/Rust-for-Linux/pin-init/status.svg)](https://deps.rs/repo/github/Rust-for-Linux/pin-init) +![License](https://img.shields.io/crates/l/pin-init) +[![Toolchain](https://img.shields.io/badge/toolchain-nightly-red)](#nightly-only) +![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/Rust-for-Linux/pin-init/test.yml) +# `pin-init` + + + +Library to safely and fallibly initialize pinned `struct`s using in-place constructors. + +[Pinning][pinning] is Rust's way of ensuring data does not move. + +It also allows in-place initialization of big `struct`s that would otherwise produce a stack +overflow. + +This library's main use-case is in [Rust-for-Linux]. Although this version can be used +standalone. + +There are cases when you want to in-place initialize a struct. For example when it is very big +and moving it from the stack is not an option, because it is bigger than the stack itself. +Another reason would be that you need the address of the object to initialize it. This stands +in direct conflict with Rust's normal process of first initializing an object and then moving +it into it's final memory location. For more information, see +. + +This library allows you to do in-place initialization safely. + +### Nightly Needed for `alloc` feature + +This library requires the [`allocator_api` unstable feature] when the `alloc` feature is +enabled and thus this feature can only be used with a nightly compiler. When enabling the +`alloc` feature, the user will be required to activate `allocator_api` as well. + +[`allocator_api` unstable feature]: https://doc.rust-lang.org/nightly/unstable-book/library-features/allocator-api.html + +The feature is enabled by default, thus by default `pin-init` will require a nightly compiler. +However, using the crate on stable compilers is possible by disabling `alloc`. In practice this +will require the `std` feature, because stable compilers have neither `Box` nor `Arc` in no-std +mode. + +## Overview + +To initialize a `struct` with an in-place constructor you will need two things: +- an in-place constructor, +- a memory location that can hold your `struct` (this can be the [stack], an [`Arc`], + [`Box`] or any other smart pointer that supports this library). + +To get an in-place constructor there are generally three options: +- directly creating an in-place constructor using the [`pin_init!`] macro, +- a custom function/macro returning an in-place constructor provided by someone else, +- using the unsafe function [`pin_init_from_closure()`] to manually create an initializer. + +Aside from pinned initialization, this library also supports in-place construction without +pinning, the macros/types/functions are generally named like the pinned variants without the +`pin_` prefix. + +## Examples + +Throughout the examples we will often make use of the `CMutex` type which can be found in +`../examples/mutex.rs`. It is essentially a userland rebuild of the `struct mutex` type from +the Linux kernel. It also uses a wait list and a basic spinlock. Importantly the wait list +requires it to be pinned to be locked and thus is a prime candidate for using this library. + +### Using the [`pin_init!`] macro + +If you want to use [`PinInit`], then you will have to annotate your `struct` with +`#[`[`pin_data`]`]`. It is a macro that uses `#[pin]` as a marker for +[structurally pinned fields]. After doing this, you can then create an in-place constructor via +[`pin_init!`]. The syntax is almost the same as normal `struct` initializers. The difference is +that you need to write `<-` instead of `:` for fields that you want to initialize in-place. + +```rust +use pin_init::{pin_data, pin_init, InPlaceInit}; + +#[pin_data] +struct Foo { + #[pin] + a: CMutex, + b: u32, +} + +let foo = pin_init!(Foo { + a <- CMutex::new(42), + b: 24, +}); +``` + +`foo` now is of the type [`impl PinInit`]. We can now use any smart pointer that we like +(or just the stack) to actually initialize a `Foo`: + +```rust +let foo: Result>, AllocError> = Box::pin_init(foo); +``` + +For more information see the [`pin_init!`] macro. + +### Using a custom function/macro that returns an initializer + +Many types that use this library supply a function/macro that returns an initializer, because +the above method only works for types where you can access the fields. + +```rust +let mtx: Result>>, _> = Arc::pin_init(CMutex::new(42)); +``` + +To declare an init macro/function you just return an [`impl PinInit`]: + +```rust +#[pin_data] +struct DriverData { + #[pin] + status: CMutex, + buffer: Box<[u8; 1_000_000]>, +} + +impl DriverData { + fn new() -> impl PinInit { + try_pin_init!(Self { + status <- CMutex::new(0), + buffer: Box::init(pin_init::zeroed())?, + }? Error) + } +} +``` + +### Manual creation of an initializer + +Often when working with primitives the previous approaches are not sufficient. That is where +[`pin_init_from_closure()`] comes in. This `unsafe` function allows you to create a +[`impl PinInit`] directly from a closure. Of course you have to ensure that the closure +actually does the initialization in the correct way. Here are the things to look out for +(we are calling the parameter to the closure `slot`): +- when the closure returns `Ok(())`, then it has completed the initialization successfully, so + `slot` now contains a valid bit pattern for the type `T`, +- when the closure returns `Err(e)`, then the caller may deallocate the memory at `slot`, so + you need to take care to clean up anything if your initialization fails mid-way, +- you may assume that `slot` will stay pinned even after the closure returns until `drop` of + `slot` gets called. + +```rust +use pin_init::{pin_data, pinned_drop, PinInit, PinnedDrop, pin_init_from_closure}; +use core::{ + ptr::addr_of_mut, + marker::PhantomPinned, + cell::UnsafeCell, + pin::Pin, + mem::MaybeUninit, +}; +mod bindings { + #[repr(C)] + pub struct foo { + /* fields from C ... */ + } + extern "C" { + pub fn init_foo(ptr: *mut foo); + pub fn destroy_foo(ptr: *mut foo); + #[must_use = "you must check the error return code"] + pub fn enable_foo(ptr: *mut foo, flags: u32) -> i32; + } +} + +/// # Invariants +/// +/// `foo` is always initialized +#[pin_data(PinnedDrop)] +pub struct RawFoo { + #[pin] + _p: PhantomPinned, + #[pin] + foo: UnsafeCell>, +} + +impl RawFoo { + pub fn new(flags: u32) -> impl PinInit { + // SAFETY: + // - when the closure returns `Ok(())`, then it has successfully initialized and + // enabled `foo`, + // - when it returns `Err(e)`, then it has cleaned up before + unsafe { + pin_init_from_closure(move |slot: *mut Self| { + // `slot` contains uninit memory, avoid creating a reference. + let foo = addr_of_mut!((*slot).foo); + let foo = UnsafeCell::raw_get(foo).cast::(); + + // Initialize the `foo` + bindings::init_foo(foo); + + // Try to enable it. + let err = bindings::enable_foo(foo, flags); + if err != 0 { + // Enabling has failed, first clean up the foo and then return the error. + bindings::destroy_foo(foo); + Err(err) + } else { + // All fields of `RawFoo` have been initialized, since `_p` is a ZST. + Ok(()) + } + }) + } + } +} + +#[pinned_drop] +impl PinnedDrop for RawFoo { + fn drop(self: Pin<&mut Self>) { + // SAFETY: Since `foo` is initialized, destroying is safe. + unsafe { bindings::destroy_foo(self.foo.get().cast::()) }; + } +} +``` + +For more information on how to use [`pin_init_from_closure()`], take a look at the uses inside +the `kernel` crate. The [`sync`] module is a good starting point. + +[`sync`]: https://rust.docs.kernel.org/kernel/sync/index.html +[pinning]: https://doc.rust-lang.org/std/pin/index.html +[structurally pinned fields]: https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field +[stack]: https://docs.rs/pin-init/latest/pin_init/macro.stack_pin_init.html +[`Arc`]: https://doc.rust-lang.org/stable/alloc/sync/struct.Arc.html +[`Box`]: https://doc.rust-lang.org/stable/alloc/boxed/struct.Box.html +[`impl PinInit`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html +[`impl PinInit`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html +[`impl Init`]: https://docs.rs/pin-init/latest/pin_init/trait.Init.html +[Rust-for-Linux]: https://rust-for-linux.com/ + + -- 2.51.0 From 1ab10101cd311703d8d53ace36d96c4cfd406a69 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 8 Mar 2025 11:05:35 +0000 Subject: [PATCH 06/16] rust: pin-init: re-enable doctests The pin-init crate is now compiled in a standalone fashion, so revert the earlier commit that disabled the doctests in pin-init in order to avoid build errors while transitioning the crate into a standalone version. Signed-off-by: Benno Lossin Reviewed-by: Fiona Behrens Reviewed-by: Andreas Hindborg Tested-by: Andreas Hindborg Link: https://lore.kernel.org/r/20250308110339.2997091-22-benno.lossin@proton.me Signed-off-by: Miguel Ojeda --- rust/pin-init/src/lib.rs | 54 ++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 45880ffa81bb..f36b8f8e8730 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -63,7 +63,7 @@ //! [`pin_init!`]. The syntax is almost the same as normal `struct` initializers. The difference is //! that you need to write `<-` instead of `:` for fields that you want to initialize in-place. //! -//! ```rust,ignore +//! ```rust //! # #![expect(clippy::disallowed_names)] //! # #![feature(allocator_api)] //! # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; @@ -87,7 +87,7 @@ //! `foo` now is of the type [`impl PinInit`]. We can now use any smart pointer that we like //! (or just the stack) to actually initialize a `Foo`: //! -//! ```rust,ignore +//! ```rust //! # #![expect(clippy::disallowed_names)] //! # #![feature(allocator_api)] //! # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; @@ -115,7 +115,7 @@ //! Many types that use this library supply a function/macro that returns an initializer, because //! the above method only works for types where you can access the fields. //! -//! ```rust,ignore +//! ```rust //! # #![feature(allocator_api)] //! # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; //! # use pin_init::*; @@ -126,7 +126,7 @@ //! //! To declare an init macro/function you just return an [`impl PinInit`]: //! -//! ```rust,ignore +//! ```rust //! # #![feature(allocator_api)] //! # use pin_init::*; //! # #[path = "../examples/error.rs"] mod error; use error::Error; @@ -162,7 +162,7 @@ //! - you may assume that `slot` will stay pinned even after the closure returns until `drop` of //! `slot` gets called. //! -//! ```rust,ignore +//! ```rust //! # #![feature(extern_types)] //! use pin_init::{pin_data, pinned_drop, PinInit, PinnedDrop, pin_init_from_closure}; //! use core::{ @@ -306,7 +306,7 @@ pub use alloc::InPlaceInit; /// /// # Examples /// -/// ```ignore +/// ``` /// # #![feature(allocator_api)] /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; /// use pin_init::pin_data; @@ -323,7 +323,7 @@ pub use alloc::InPlaceInit; /// } /// ``` /// -/// ```ignore +/// ``` /// # #![feature(allocator_api)] /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; /// # mod bindings { pub struct info; pub unsafe fn destroy_info(_: *mut info) {} } @@ -357,7 +357,7 @@ pub use ::pin_init_internal::pin_data; /// /// # Examples /// -/// ```ignore +/// ``` /// # #![feature(allocator_api)] /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; /// # mod bindings { pub struct info; pub unsafe fn destroy_info(_: *mut info) {} } @@ -391,7 +391,7 @@ pub use ::pin_init_internal::pinned_drop; /// /// # Examples /// -/// ```ignore +/// ``` /// use pin_init::Zeroable; /// /// #[derive(Zeroable)] @@ -407,7 +407,7 @@ pub use ::pin_init_internal::Zeroable; /// /// # Examples /// -/// ```rust,ignore +/// ```rust /// # #![expect(clippy::disallowed_names)] /// # #![feature(allocator_api)] /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; @@ -459,7 +459,7 @@ macro_rules! stack_pin_init { /// /// # Examples /// -/// ```rust,ignore +/// ```rust /// # #![expect(clippy::disallowed_names)] /// # #![feature(allocator_api)] /// # #[path = "../examples/error.rs"] mod error; use error::Error; @@ -486,7 +486,7 @@ macro_rules! stack_pin_init { /// println!("a: {}", &*foo.a.lock()); /// ``` /// -/// ```rust,ignore +/// ```rust /// # #![expect(clippy::disallowed_names)] /// # #![feature(allocator_api)] /// # #[path = "../examples/error.rs"] mod error; use error::Error; @@ -539,7 +539,7 @@ macro_rules! stack_try_pin_init { /// /// The syntax is almost identical to that of a normal `struct` initializer: /// -/// ```rust,ignore +/// ```rust /// # use pin_init::*; /// # use core::pin::Pin; /// #[pin_data] @@ -583,7 +583,7 @@ macro_rules! stack_try_pin_init { /// /// To create an initializer function, simply declare it like this: /// -/// ```rust,ignore +/// ```rust /// # use pin_init::*; /// # use core::pin::Pin; /// # #[pin_data] @@ -609,7 +609,7 @@ macro_rules! stack_try_pin_init { /// /// Users of `Foo` can now create it like this: /// -/// ```rust,ignore +/// ```rust /// # #![expect(clippy::disallowed_names)] /// # use pin_init::*; /// # use core::pin::Pin; @@ -637,7 +637,7 @@ macro_rules! stack_try_pin_init { /// /// They can also easily embed it into their own `struct`s: /// -/// ```rust,ignore +/// ```rust /// # use pin_init::*; /// # use core::pin::Pin; /// # #[pin_data] @@ -696,7 +696,7 @@ macro_rules! stack_try_pin_init { /// /// For instance: /// -/// ```rust,ignore +/// ```rust /// # use pin_init::*; /// # use core::{ptr::addr_of_mut, marker::PhantomPinned}; /// #[pin_data] @@ -750,7 +750,7 @@ macro_rules! pin_init { /// /// # Examples /// -/// ```rust,ignore +/// ```rust /// # #![feature(allocator_api)] /// # #[path = "../examples/error.rs"] mod error; use error::Error; /// use pin_init::{pin_data, try_pin_init, PinInit, InPlaceInit, zeroed}; @@ -857,7 +857,7 @@ macro_rules! init { /// /// # Examples /// -/// ```rust,ignore +/// ```rust /// # #![feature(allocator_api)] /// # use core::alloc::AllocError; /// # use pin_init::InPlaceInit; @@ -904,7 +904,7 @@ macro_rules! try_init { /// # Example /// /// This will succeed: -/// ```ignore +/// ``` /// use pin_init::{pin_data, assert_pinned}; /// /// #[pin_data] @@ -917,7 +917,7 @@ macro_rules! try_init { /// ``` /// /// This will fail: -/// ```compile_fail,ignore +/// ```compile_fail /// use pin_init::{pin_data, assert_pinned}; /// /// #[pin_data] @@ -931,7 +931,7 @@ macro_rules! try_init { /// 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. -/// ```ignore +/// ``` /// # use core::pin::Pin; /// use pin_init::{pin_data, assert_pinned}; /// @@ -1018,7 +1018,7 @@ pub unsafe trait PinInit: Sized { /// /// # Examples /// - /// ```rust,ignore + /// ```rust /// # #![feature(allocator_api)] /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; /// # use pin_init::*; @@ -1116,7 +1116,7 @@ pub unsafe trait Init: PinInit { /// /// # Examples /// - /// ```rust,ignore + /// ```rust /// # #![expect(clippy::disallowed_names)] /// use pin_init::{init, zeroed, Init}; /// @@ -1229,7 +1229,7 @@ pub fn uninit() -> impl Init, E> { /// /// # Examples /// -/// ```rust,ignore +/// ```rust /// # use pin_init::*; /// use pin_init::init_array_from_fn; /// let array: Box<[usize; 1_000]> = Box::init(init_array_from_fn(|i| i)).unwrap(); @@ -1267,7 +1267,7 @@ where /// /// # Examples /// -/// ```rust,ignore +/// ```rust /// # #![feature(allocator_api)] /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; /// # use pin_init::*; @@ -1343,7 +1343,7 @@ pub trait InPlaceWrite { /// /// Use [`pinned_drop`] to implement this trait safely: /// -/// ```rust,ignore +/// ```rust /// # #![feature(allocator_api)] /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; /// # use pin_init::*; -- 2.51.0 From cf25bc61f8aecad9b0c45fe32697e35ea4b13378 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 8 Mar 2025 11:05:39 +0000 Subject: [PATCH 07/16] MAINTAINERS: add entry for the `pin-init` crate Add maintainers entry for the `pin-init` crate. This crate is already being maintained by me, but until now there existed two different versions: the version inside of the kernel tree and a user-space version at [1]. The previous patches synchronized these two versions to reduce the maintenance burden. In order to keep them synchronized from now on, separate the maintenance from other Rust code. Link: https://github.com/Rust-for-Linux/pin-init [1] Signed-off-by: Benno Lossin Link: https://lore.kernel.org/r/20250308110339.2997091-23-benno.lossin@proton.me Signed-off-by: Miguel Ojeda --- MAINTAINERS | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 007ca67cc830..72b88f1638a2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20737,6 +20737,19 @@ T: git https://github.com/Rust-for-Linux/linux.git alloc-next F: rust/kernel/alloc.rs F: rust/kernel/alloc/ +RUST [PIN-INIT] +M: Benno Lossin +L: rust-for-linux@vger.kernel.org +S: Maintained +W: https://rust-for-linux.com/pin-init +B: https://github.com/Rust-for-Linux/pin-init/issues +C: zulip://rust-for-linux.zulipchat.com +P: rust/pin-init/CONTRIBUTING.md +T: git https://github.com/Rust-for-Linux/linux.git pin-init-next +F: rust/kernel/init.rs +F: rust/pin-init/ +K: \bpin-init\b|pin_init\b|PinInit + RXRPC SOCKETS (AF_RXRPC) M: David Howells M: Marc Dionne -- 2.51.0 From 6b2dab17d6fad9d94faae45b46bef307d8560cdf Mon Sep 17 00:00:00 2001 From: =?utf8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 8 Feb 2025 14:31:14 +0100 Subject: [PATCH 08/16] rust: pass correct target to bindgen on Usermode Linux MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Usermode Linux uses "um" as primary architecture name and the underlying physical architecture is provided in "SUBARCH". Resolve the target architecture flags through that underlying architecture. This is the same pattern as used by scripts/Makefile.clang from which the bindgen flags are derived. [ David says: (...) this is enough to get Rust-for-Linux working with gcc under 64-bit UML on my system. - Miguel ] Signed-off-by: Thomas Weißschuh Reviewed-by: David Gow Acked-by: Johannes Berg Link: https://lore.kernel.org/r/20250208-rust-kunit-v1-1-94a026be6d72@weissschuh.net Signed-off-by: Miguel Ojeda --- rust/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/Makefile b/rust/Makefile index e761a8cc3bd5..b9cc810764e9 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -274,6 +274,7 @@ bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \ # Derived from `scripts/Makefile.clang`. BINDGEN_TARGET_x86 := x86_64-linux-gnu BINDGEN_TARGET_arm64 := aarch64-linux-gnu +BINDGEN_TARGET_um := $(BINDGEN_TARGET_$(SUBARCH)) BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH)) # All warnings are inhibited since GCC builds are very experimental, -- 2.51.0 From fb625227d540ddead4d21813410a116e9452d232 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 8 Feb 2025 14:31:15 +0100 Subject: [PATCH 09/16] rust: add kunitconfig MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The kunitconfig file in a directory is used by kunit.py to enable all necessary kernel configurations to run the tests in that subdirectory. Add such a file for rust/. Signed-off-by: Thomas Weißschuh Reviewed-by: David Gow Link: https://lore.kernel.org/r/20250208-rust-kunit-v1-2-94a026be6d72@weissschuh.net Signed-off-by: Miguel Ojeda --- rust/.kunitconfig | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 rust/.kunitconfig diff --git a/rust/.kunitconfig b/rust/.kunitconfig new file mode 100644 index 000000000000..9e72a5ab03c9 --- /dev/null +++ b/rust/.kunitconfig @@ -0,0 +1,3 @@ +CONFIG_KUNIT=y +CONFIG_RUST=y +CONFIG_RUST_KERNEL_DOCTESTS=y -- 2.51.0 From 22097b966f5d2be93b315c791a26d4ed9b37f195 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Exp=C3=B3sito?= Date: Fri, 7 Mar 2025 17:00:56 +0800 Subject: [PATCH 10/16] rust: kunit: add KUnit case and suite macros MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add a couple of Rust const functions and macros to allow to develop KUnit tests without relying on generated C code: - The `kunit_unsafe_test_suite!` Rust macro is similar to the `kunit_test_suite` C macro. It requires a NULL-terminated array of test cases (see below). - The `kunit_case` Rust function is similar to the `KUNIT_CASE` C macro. It generates as case from the name and function. - The `kunit_case_null` Rust function generates a NULL test case, which is to be used as delimiter in `kunit_test_suite!`. While these functions and macros can be used on their own, a future patch will introduce another macro to create KUnit tests using a user-space like syntax. Signed-off-by: José Expósito Co-developed-by: Matt Gilbride Signed-off-by: Matt Gilbride Co-developed-by: Miguel Ojeda Signed-off-by: Miguel Ojeda Co-developed-by: David Gow Signed-off-by: David Gow Link: https://lore.kernel.org/r/20250307090103.918788-2-davidgow@google.com [ Applied Markdown in comment. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/kunit.rs | 124 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 824da0e9738a..e777a4e03e4b 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -161,3 +161,127 @@ macro_rules! kunit_assert_eq { $crate::kunit_assert!($name, $file, $diff, $left == $right); }}; } + +/// Represents an individual test case. +/// +/// The [`kunit_unsafe_test_suite!`] macro expects a NULL-terminated list of valid test cases. +/// Use [`kunit_case_null`] to generate such a delimiter. +#[doc(hidden)] +pub const fn kunit_case( + name: &'static kernel::str::CStr, + run_case: unsafe extern "C" fn(*mut kernel::bindings::kunit), +) -> kernel::bindings::kunit_case { + kernel::bindings::kunit_case { + run_case: Some(run_case), + name: name.as_char_ptr(), + attr: kernel::bindings::kunit_attributes { + speed: kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL, + }, + generate_params: None, + status: kernel::bindings::kunit_status_KUNIT_SUCCESS, + module_name: core::ptr::null_mut(), + log: core::ptr::null_mut(), + } +} + +/// Represents the NULL test case delimiter. +/// +/// The [`kunit_unsafe_test_suite!`] macro expects a NULL-terminated list of test cases. This +/// function returns such a delimiter. +#[doc(hidden)] +pub const fn kunit_case_null() -> kernel::bindings::kunit_case { + kernel::bindings::kunit_case { + run_case: None, + name: core::ptr::null_mut(), + generate_params: None, + attr: kernel::bindings::kunit_attributes { + speed: kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL, + }, + status: kernel::bindings::kunit_status_KUNIT_SUCCESS, + module_name: core::ptr::null_mut(), + log: core::ptr::null_mut(), + } +} + +/// Registers a KUnit test suite. +/// +/// # Safety +/// +/// `test_cases` must be a NULL terminated array of valid test cases, +/// whose lifetime is at least that of the test suite (i.e., static). +/// +/// # Examples +/// +/// ```ignore +/// extern "C" fn test_fn(_test: *mut kernel::bindings::kunit) { +/// let actual = 1 + 1; +/// let expected = 2; +/// assert_eq!(actual, expected); +/// } +/// +/// static mut KUNIT_TEST_CASES: [kernel::bindings::kunit_case; 2] = [ +/// kernel::kunit::kunit_case(kernel::c_str!("name"), test_fn), +/// kernel::kunit::kunit_case_null(), +/// ]; +/// kernel::kunit_unsafe_test_suite!(suite_name, KUNIT_TEST_CASES); +/// ``` +#[doc(hidden)] +#[macro_export] +macro_rules! kunit_unsafe_test_suite { + ($name:ident, $test_cases:ident) => { + const _: () = { + const KUNIT_TEST_SUITE_NAME: [::kernel::ffi::c_char; 256] = { + let name_u8 = ::core::stringify!($name).as_bytes(); + let mut ret = [0; 256]; + + if name_u8.len() > 255 { + panic!(concat!( + "The test suite name `", + ::core::stringify!($name), + "` exceeds the maximum length of 255 bytes." + )); + } + + let mut i = 0; + while i < name_u8.len() { + ret[i] = name_u8[i] as ::kernel::ffi::c_char; + i += 1; + } + + ret + }; + + static mut KUNIT_TEST_SUITE: ::kernel::bindings::kunit_suite = + ::kernel::bindings::kunit_suite { + name: KUNIT_TEST_SUITE_NAME, + #[allow(unused_unsafe)] + // SAFETY: `$test_cases` is passed in by the user, and + // (as documented) must be valid for the lifetime of + // the suite (i.e., static). + test_cases: unsafe { + ::core::ptr::addr_of_mut!($test_cases) + .cast::<::kernel::bindings::kunit_case>() + }, + suite_init: None, + suite_exit: None, + init: None, + exit: None, + attr: ::kernel::bindings::kunit_attributes { + speed: ::kernel::bindings::kunit_speed_KUNIT_SPEED_NORMAL, + }, + status_comment: [0; 256usize], + debugfs: ::core::ptr::null_mut(), + log: ::core::ptr::null_mut(), + suite_init_err: 0, + is_init: false, + }; + + #[used] + #[allow(unused_unsafe)] + #[cfg_attr(not(target_os = "macos"), link_section = ".kunit_test_suites")] + static mut KUNIT_TEST_SUITE_ENTRY: *const ::kernel::bindings::kunit_suite = + // SAFETY: `KUNIT_TEST_SUITE` is static. + unsafe { ::core::ptr::addr_of_mut!(KUNIT_TEST_SUITE) }; + }; + }; +} -- 2.51.0 From c0010452893e07e032427e88f6b7b4bf7ac42e95 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Exp=C3=B3sito?= Date: Fri, 7 Mar 2025 17:00:57 +0800 Subject: [PATCH 11/16] rust: macros: add macro to easily run KUnit tests MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add a new procedural macro (`#[kunit_tests(kunit_test_suit_name)]`) to run KUnit tests using a user-space like syntax. The macro, that should be used on modules, transforms every `#[test]` in a `kunit_case!` and adds a `kunit_unsafe_test_suite!` registering all of them. The only difference with user-space tests is that instead of using `#[cfg(test)]`, `#[kunit_tests(kunit_test_suit_name)]` is used. Note that `#[cfg(CONFIG_KUNIT)]` is added so the test module is not compiled when `CONFIG_KUNIT` is set to `n`. Reviewed-by: David Gow Signed-off-by: José Expósito Co-developed-by: Boqun Feng Signed-off-by: Boqun Feng Co-developed-by: Miguel Ojeda Signed-off-by: Miguel Ojeda Reviewed-by: Tamir Duberstein Signed-off-by: David Gow Link: https://lore.kernel.org/r/20250307090103.918788-3-davidgow@google.com [ Removed spurious (in rendered form) newline in docs. - Miguel ] Signed-off-by: Miguel Ojeda --- MAINTAINERS | 1 + rust/kernel/kunit.rs | 11 +++ rust/macros/kunit.rs | 161 +++++++++++++++++++++++++++++++++++++++++++ rust/macros/lib.rs | 28 ++++++++ 4 files changed, 201 insertions(+) create mode 100644 rust/macros/kunit.rs diff --git a/MAINTAINERS b/MAINTAINERS index 72b88f1638a2..af59c90e6bd6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12679,6 +12679,7 @@ F: Documentation/dev-tools/kunit/ F: include/kunit/ F: lib/kunit/ F: rust/kernel/kunit.rs +F: rust/macros/kunit.rs F: scripts/rustdoc_test_* F: tools/testing/kunit/ diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index e777a4e03e4b..50cd45912d6e 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -40,6 +40,8 @@ pub fn info(args: fmt::Arguments<'_>) { } } +use macros::kunit_tests; + /// Asserts that a boolean expression is `true` at runtime. /// /// Public but hidden since it should only be used from generated tests. @@ -285,3 +287,12 @@ macro_rules! kunit_unsafe_test_suite { }; }; } + +#[kunit_tests(rust_kernel_kunit)] +mod tests { + #[test] + fn rust_test_kunit_example_test() { + #![expect(clippy::eq_op)] + assert_eq!(1 + 1, 2); + } +} diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs new file mode 100644 index 000000000000..4f553ecf40c0 --- /dev/null +++ b/rust/macros/kunit.rs @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Procedural macro to run KUnit tests using a user-space like syntax. +//! +//! Copyright (c) 2023 José Expósito + +use proc_macro::{Delimiter, Group, TokenStream, TokenTree}; +use std::fmt::Write; + +pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { + let attr = attr.to_string(); + + if attr.is_empty() { + panic!("Missing test name in `#[kunit_tests(test_name)]` macro") + } + + if attr.len() > 255 { + panic!( + "The test suite name `{}` exceeds the maximum length of 255 bytes", + attr + ) + } + + let mut tokens: Vec<_> = ts.into_iter().collect(); + + // Scan for the `mod` keyword. + tokens + .iter() + .find_map(|token| match token { + TokenTree::Ident(ident) => match ident.to_string().as_str() { + "mod" => Some(true), + _ => None, + }, + _ => None, + }) + .expect("`#[kunit_tests(test_name)]` attribute should only be applied to modules"); + + // Retrieve the main body. The main body should be the last token tree. + let body = match tokens.pop() { + Some(TokenTree::Group(group)) if group.delimiter() == Delimiter::Brace => group, + _ => panic!("Cannot locate main body of module"), + }; + + // Get the functions set as tests. Search for `[test]` -> `fn`. + let mut body_it = body.stream().into_iter(); + let mut tests = Vec::new(); + while let Some(token) = body_it.next() { + match token { + TokenTree::Group(ident) if ident.to_string() == "[test]" => match body_it.next() { + Some(TokenTree::Ident(ident)) if ident.to_string() == "fn" => { + let test_name = match body_it.next() { + Some(TokenTree::Ident(ident)) => ident.to_string(), + _ => continue, + }; + tests.push(test_name); + } + _ => continue, + }, + _ => (), + } + } + + // Add `#[cfg(CONFIG_KUNIT)]` before the module declaration. + let config_kunit = "#[cfg(CONFIG_KUNIT)]".to_owned().parse().unwrap(); + tokens.insert( + 0, + TokenTree::Group(Group::new(Delimiter::None, config_kunit)), + ); + + // Generate the test KUnit test suite and a test case for each `#[test]`. + // The code generated for the following test module: + // + // ``` + // #[kunit_tests(kunit_test_suit_name)] + // mod tests { + // #[test] + // fn foo() { + // assert_eq!(1, 1); + // } + // + // #[test] + // fn bar() { + // assert_eq!(2, 2); + // } + // } + // ``` + // + // Looks like: + // + // ``` + // unsafe extern "C" fn kunit_rust_wrapper_foo(_test: *mut kernel::bindings::kunit) { foo(); } + // unsafe extern "C" fn kunit_rust_wrapper_bar(_test: *mut kernel::bindings::kunit) { bar(); } + // + // static mut TEST_CASES: [kernel::bindings::kunit_case; 3] = [ + // kernel::kunit::kunit_case(kernel::c_str!("foo"), kunit_rust_wrapper_foo), + // kernel::kunit::kunit_case(kernel::c_str!("bar"), kunit_rust_wrapper_bar), + // kernel::kunit::kunit_case_null(), + // ]; + // + // kernel::kunit_unsafe_test_suite!(kunit_test_suit_name, TEST_CASES); + // ``` + let mut kunit_macros = "".to_owned(); + let mut test_cases = "".to_owned(); + for test in &tests { + let kunit_wrapper_fn_name = format!("kunit_rust_wrapper_{}", test); + let kunit_wrapper = format!( + "unsafe extern \"C\" fn {}(_test: *mut kernel::bindings::kunit) {{ {}(); }}", + kunit_wrapper_fn_name, test + ); + writeln!(kunit_macros, "{kunit_wrapper}").unwrap(); + writeln!( + test_cases, + " kernel::kunit::kunit_case(kernel::c_str!(\"{}\"), {}),", + test, kunit_wrapper_fn_name + ) + .unwrap(); + } + + writeln!(kunit_macros).unwrap(); + writeln!( + kunit_macros, + "static mut TEST_CASES: [kernel::bindings::kunit_case; {}] = [\n{test_cases} kernel::kunit::kunit_case_null(),\n];", + tests.len() + 1 + ) + .unwrap(); + + writeln!( + kunit_macros, + "kernel::kunit_unsafe_test_suite!({attr}, TEST_CASES);" + ) + .unwrap(); + + // Remove the `#[test]` macros. + // We do this at a token level, in order to preserve span information. + let mut new_body = vec![]; + let mut body_it = body.stream().into_iter(); + + while let Some(token) = body_it.next() { + match token { + TokenTree::Punct(ref c) if c.as_char() == '#' => match body_it.next() { + Some(TokenTree::Group(group)) if group.to_string() == "[test]" => (), + Some(next) => { + new_body.extend([token, next]); + } + _ => { + new_body.push(token); + } + }, + _ => { + new_body.push(token); + } + } + } + + let mut new_body = TokenStream::from_iter(new_body); + new_body.extend::(kunit_macros.parse().unwrap()); + + tokens.push(TokenTree::Group(Group::new(Delimiter::Brace, new_body))); + + tokens.into_iter().collect() +} diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index f0f8c9232748..9acaa68c974e 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -11,6 +11,7 @@ mod quote; mod concat_idents; mod export; mod helpers; +mod kunit; mod module; mod paste; mod vtable; @@ -392,3 +393,30 @@ pub fn paste(input: TokenStream) -> TokenStream { paste::expand(&mut tokens); tokens.into_iter().collect() } + +/// Registers a KUnit test suite and its test cases using a user-space like syntax. +/// +/// This macro should be used on modules. If `CONFIG_KUNIT` (in `.config`) is `n`, the target module +/// is ignored. +/// +/// # Examples +/// +/// ```ignore +/// # use macros::kunit_tests; +/// #[kunit_tests(kunit_test_suit_name)] +/// mod tests { +/// #[test] +/// fn foo() { +/// assert_eq!(1, 1); +/// } +/// +/// #[test] +/// fn bar() { +/// assert_eq!(2, 2); +/// } +/// } +/// ``` +#[proc_macro_attribute] +pub fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { + kunit::kunit_tests(attr, ts) +} -- 2.51.0 From 100af58c8d5822750ef9ba65f5d5ea3367c669de Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Exp=C3=B3sito?= Date: Fri, 7 Mar 2025 17:00:58 +0800 Subject: [PATCH 12/16] rust: kunit: allow to know if we are in a test MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit In some cases, we need to call test-only code from outside the test case, for example, to mock a function or a module. In order to check whether we are in a test or not, we need to test if `CONFIG_KUNIT` is set. Unfortunately, we cannot rely only on this condition because: - a test could be running in another thread, - some distros compile KUnit in production kernels, so checking at runtime that `current->kunit_test != NULL` is required. Forturately, KUnit provides an optimised check in `kunit_get_current_test()`, which checks CONFIG_KUNIT, a global static key, and then the current thread's running KUnit test. Add a safe wrapper function around this to know whether or not we are in a KUnit test and examples showing how to mock a function and a module. Signed-off-by: José Expósito Co-developed-by: Miguel Ojeda Signed-off-by: Miguel Ojeda Co-developed-by: David Gow Signed-off-by: David Gow Link: https://lore.kernel.org/r/20250307090103.918788-4-davidgow@google.com Signed-off-by: Miguel Ojeda --- rust/kernel/kunit.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 50cd45912d6e..1604fb6a5b1b 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -288,11 +288,47 @@ macro_rules! kunit_unsafe_test_suite { }; } +/// Returns whether we are currently running a KUnit test. +/// +/// In some cases, you need to call test-only code from outside the test case, for example, to +/// create a function mock. This function allows to change behavior depending on whether we are +/// currently running a KUnit test or not. +/// +/// # Examples +/// +/// This example shows how a function can be mocked to return a well-known value while testing: +/// +/// ``` +/// # use kernel::kunit::in_kunit_test; +/// fn fn_mock_example(n: i32) -> i32 { +/// if in_kunit_test() { +/// return 100; +/// } +/// +/// n + 1 +/// } +/// +/// let mock_res = fn_mock_example(5); +/// assert_eq!(mock_res, 100); +/// ``` +pub fn in_kunit_test() -> bool { + // SAFETY: `kunit_get_current_test()` is always safe to call (it has fallbacks for + // when KUnit is not enabled). + !unsafe { bindings::kunit_get_current_test() }.is_null() +} + #[kunit_tests(rust_kernel_kunit)] mod tests { + use super::*; + #[test] fn rust_test_kunit_example_test() { #![expect(clippy::eq_op)] assert_eq!(1 + 1, 2); } + + #[test] + fn rust_test_kunit_in_kunit_test() { + assert!(in_kunit_test()); + } } -- 2.51.0 From e385e94a8bc38ffb1da1a735eb1d7f290c8852f0 Mon Sep 17 00:00:00 2001 From: Abdiel Janulgue Date: Mon, 17 Mar 2025 20:52:08 +0200 Subject: [PATCH 13/16] rust: error: Add EOVERFLOW Trivial addition for missing EOVERFLOW error. This is used by a subsequent patch that might require returning EOVERFLOW as a result of `checked_mul`. Reviewed-by: Alice Ryhl Reviewed-by: Andreas Hindborg Signed-off-by: Abdiel Janulgue Acked-by: Danilo Krummrich Link: https://lore.kernel.org/r/20250317185345.2608976-2-abdiel.janulgue@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 376f6a6ae5e3..30014d507ed3 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -64,6 +64,7 @@ pub mod code { declare_err!(EPIPE, "Broken pipe."); declare_err!(EDOM, "Math argument out of domain of func."); declare_err!(ERANGE, "Math result not representable."); + declare_err!(EOVERFLOW, "Value too large for defined data type."); declare_err!(ERESTARTSYS, "Restart the system call."); declare_err!(ERESTARTNOINTR, "System call was interrupted by a signal and will be restarted."); declare_err!(ERESTARTNOHAND, "Restart if no handler."); -- 2.51.0 From ad2907b4e308a93deac93ff408f8bbbcac333905 Mon Sep 17 00:00:00 2001 From: Abdiel Janulgue Date: Mon, 17 Mar 2025 20:52:09 +0200 Subject: [PATCH 14/16] rust: add dma coherent allocator abstraction Add a simple dma coherent allocator rust abstraction. Based on Andreas Hindborg's dma abstractions from the rnvme driver, which was also based on earlier work by Wedson Almeida Filho. Reviewed-by: Alice Ryhl Signed-off-by: Abdiel Janulgue Acked-by: Danilo Krummrich Link: https://lore.kernel.org/r/20250317185345.2608976-3-abdiel.janulgue@gmail.com Nacked-by: Christoph Hellwig [ Removed period. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/bindings/bindings_helper.h | 1 + rust/kernel/dma.rs | 387 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 389 insertions(+) create mode 100644 rust/kernel/dma.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index ae39fc18a8bf..ccb988340df6 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs new file mode 100644 index 000000000000..9d00f9c49f47 --- /dev/null +++ b/rust/kernel/dma.rs @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Direct memory access (DMA). +//! +//! C header: [`include/linux/dma-mapping.h`](srctree/include/linux/dma-mapping.h) + +use crate::{ + bindings, build_assert, + device::Device, + error::code::*, + error::Result, + transmute::{AsBytes, FromBytes}, + types::ARef, +}; + +/// Possible attributes associated with a DMA mapping. +/// +/// They can be combined with the operators `|`, `&`, and `!`. +/// +/// Values can be used from the [`attrs`] module. +/// +/// # Examples +/// +/// ``` +/// use kernel::device::Device; +/// use kernel::dma::{attrs::*, CoherentAllocation}; +/// +/// # fn test(dev: &Device) -> Result { +/// let attribs = DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_WARN; +/// let c: CoherentAllocation = +/// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, attribs)?; +/// # Ok::<(), Error>(()) } +/// ``` +#[derive(Clone, Copy, PartialEq)] +#[repr(transparent)] +pub struct Attrs(u32); + +impl Attrs { + /// Get the raw representation of this attribute. + pub(crate) fn as_raw(self) -> crate::ffi::c_ulong { + self.0 as _ + } + + /// Check whether `flags` is contained in `self`. + pub fn contains(self, flags: Attrs) -> bool { + (self & flags) == flags + } +} + +impl core::ops::BitOr for Attrs { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl core::ops::BitAnd for Attrs { + type Output = Self; + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } +} + +impl core::ops::Not for Attrs { + type Output = Self; + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +/// DMA mapping attributes. +pub mod attrs { + use super::Attrs; + + /// Specifies that reads and writes to the mapping may be weakly ordered, that is that reads + /// and writes may pass each other. + pub const DMA_ATTR_WEAK_ORDERING: Attrs = Attrs(bindings::DMA_ATTR_WEAK_ORDERING); + + /// Specifies that writes to the mapping may be buffered to improve performance. + pub const DMA_ATTR_WRITE_COMBINE: Attrs = Attrs(bindings::DMA_ATTR_WRITE_COMBINE); + + /// Lets the platform to avoid creating a kernel virtual mapping for the allocated buffer. + pub const DMA_ATTR_NO_KERNEL_MAPPING: Attrs = Attrs(bindings::DMA_ATTR_NO_KERNEL_MAPPING); + + /// Allows platform code to skip synchronization of the CPU cache for the given buffer assuming + /// that it has been already transferred to 'device' domain. + pub const DMA_ATTR_SKIP_CPU_SYNC: Attrs = Attrs(bindings::DMA_ATTR_SKIP_CPU_SYNC); + + /// Forces contiguous allocation of the buffer in physical memory. + pub const DMA_ATTR_FORCE_CONTIGUOUS: Attrs = Attrs(bindings::DMA_ATTR_FORCE_CONTIGUOUS); + + /// This is a hint to the DMA-mapping subsystem that it's probably not worth the time to try + /// to allocate memory to in a way that gives better TLB efficiency. + pub const DMA_ATTR_ALLOC_SINGLE_PAGES: Attrs = Attrs(bindings::DMA_ATTR_ALLOC_SINGLE_PAGES); + + /// This tells the DMA-mapping subsystem to suppress allocation failure reports (similarly to + /// __GFP_NOWARN). + pub const DMA_ATTR_NO_WARN: Attrs = Attrs(bindings::DMA_ATTR_NO_WARN); + + /// Used to indicate that the buffer is fully accessible at an elevated privilege level (and + /// ideally inaccessible or at least read-only at lesser-privileged levels). + pub const DMA_ATTR_PRIVILEGED: Attrs = Attrs(bindings::DMA_ATTR_PRIVILEGED); +} + +/// An abstraction of the `dma_alloc_coherent` API. +/// +/// This is an abstraction around the `dma_alloc_coherent` API which is used to allocate and map +/// large consistent DMA regions. +/// +/// A [`CoherentAllocation`] instance contains a pointer to the allocated region (in the +/// processor's virtual address space) and the device address which can be given to the device +/// as the DMA address base of the region. The region is released once [`CoherentAllocation`] +/// is dropped. +/// +/// # Invariants +/// +/// For the lifetime of an instance of [`CoherentAllocation`], the `cpu_addr` is a valid pointer +/// to an allocated region of consistent memory and `dma_handle` is the DMA address base of +/// the region. +// TODO +// +// DMA allocations potentially carry device resources (e.g.IOMMU mappings), hence for soundness +// reasons DMA allocation would need to be embedded in a `Devres` container, in order to ensure +// that device resources can never survive device unbind. +// +// However, it is neither desirable nor necessary to protect the allocated memory of the DMA +// allocation from surviving device unbind; it would require RCU read side critical sections to +// access the memory, which may require subsequent unnecessary copies. +// +// Hence, find a way to revoke the device resources of a `CoherentAllocation`, but not the +// entire `CoherentAllocation` including the allocated memory itself. +pub struct CoherentAllocation { + dev: ARef, + dma_handle: bindings::dma_addr_t, + count: usize, + cpu_addr: *mut T, + dma_attrs: Attrs, +} + +impl CoherentAllocation { + /// Allocates a region of `size_of:: * count` of consistent memory. + /// + /// # Examples + /// + /// ``` + /// use kernel::device::Device; + /// use kernel::dma::{attrs::*, CoherentAllocation}; + /// + /// # fn test(dev: &Device) -> Result { + /// let c: CoherentAllocation = + /// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, DMA_ATTR_NO_WARN)?; + /// # Ok::<(), Error>(()) } + /// ``` + pub fn alloc_attrs( + dev: &Device, + count: usize, + gfp_flags: kernel::alloc::Flags, + dma_attrs: Attrs, + ) -> Result> { + build_assert!( + core::mem::size_of::() > 0, + "It doesn't make sense for the allocated type to be a ZST" + ); + + let size = count + .checked_mul(core::mem::size_of::()) + .ok_or(EOVERFLOW)?; + let mut dma_handle = 0; + // SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`. + let ret = unsafe { + bindings::dma_alloc_attrs( + dev.as_raw(), + size, + &mut dma_handle, + gfp_flags.as_raw(), + dma_attrs.as_raw(), + ) + }; + if ret.is_null() { + return Err(ENOMEM); + } + // INVARIANT: We just successfully allocated a coherent region which is accessible for + // `count` elements, hence the cpu address is valid. We also hold a refcounted reference + // to the device. + Ok(Self { + dev: dev.into(), + dma_handle, + count, + cpu_addr: ret as *mut T, + dma_attrs, + }) + } + + /// Performs the same functionality as [`CoherentAllocation::alloc_attrs`], except the + /// `dma_attrs` is 0 by default. + pub fn alloc_coherent( + dev: &Device, + count: usize, + gfp_flags: kernel::alloc::Flags, + ) -> Result> { + CoherentAllocation::alloc_attrs(dev, count, gfp_flags, Attrs(0)) + } + + /// Returns the base address to the allocated region in the CPU's virtual address space. + pub fn start_ptr(&self) -> *const T { + self.cpu_addr + } + + /// Returns the base address to the allocated region in the CPU's virtual address space as + /// a mutable pointer. + pub fn start_ptr_mut(&mut self) -> *mut T { + self.cpu_addr + } + + /// Returns a DMA handle which may given to the device as the DMA address base of + /// the region. + pub fn dma_handle(&self) -> bindings::dma_addr_t { + self.dma_handle + } + + /// Returns a pointer to an element from the region with bounds checking. `offset` is in + /// units of `T`, not the number of bytes. + /// + /// Public but hidden since it should only be used from [`dma_read`] and [`dma_write`] macros. + #[doc(hidden)] + pub fn item_from_index(&self, offset: usize) -> Result<*mut T> { + if offset >= self.count { + return Err(EINVAL); + } + // SAFETY: + // - The pointer is valid due to type invariant on `CoherentAllocation` + // and we've just checked that the range and index is within bounds. + // - `offset` can't overflow since it is smaller than `self.count` and we've checked + // that `self.count` won't overflow early in the constructor. + Ok(unsafe { self.cpu_addr.add(offset) }) + } + + /// Reads the value of `field` and ensures that its type is [`FromBytes`]. + /// + /// # Safety + /// + /// This must be called from the [`dma_read`] macro which ensures that the `field` pointer is + /// validated beforehand. + /// + /// Public but hidden since it should only be used from [`dma_read`] macro. + #[doc(hidden)] + pub unsafe fn field_read(&self, field: *const F) -> F { + // SAFETY: + // - By the safety requirements field is valid. + // - Using read_volatile() here is not sound as per the usual rules, the usage here is + // a special exception with the following notes in place. When dealing with a potential + // race from a hardware or code outside kernel (e.g. user-space program), we need that + // read on a valid memory is not UB. Currently read_volatile() is used for this, and the + // rationale behind is that it should generate the same code as READ_ONCE() which the + // kernel already relies on to avoid UB on data races. Note that the usage of + // read_volatile() is limited to this particular case, it cannot be used to prevent + // the UB caused by racing between two kernel functions nor do they provide atomicity. + unsafe { field.read_volatile() } + } + + /// Writes a value to `field` and ensures that its type is [`AsBytes`]. + /// + /// # Safety + /// + /// This must be called from the [`dma_write`] macro which ensures that the `field` pointer is + /// validated beforehand. + /// + /// Public but hidden since it should only be used from [`dma_write`] macro. + #[doc(hidden)] + pub unsafe fn field_write(&self, field: *mut F, val: F) { + // SAFETY: + // - By the safety requirements field is valid. + // - Using write_volatile() here is not sound as per the usual rules, the usage here is + // a special exception with the following notes in place. When dealing with a potential + // race from a hardware or code outside kernel (e.g. user-space program), we need that + // write on a valid memory is not UB. Currently write_volatile() is used for this, and the + // rationale behind is that it should generate the same code as WRITE_ONCE() which the + // kernel already relies on to avoid UB on data races. Note that the usage of + // write_volatile() is limited to this particular case, it cannot be used to prevent + // the UB caused by racing between two kernel functions nor do they provide atomicity. + unsafe { field.write_volatile(val) } + } +} + +/// Note that the device configured to do DMA must be halted before this object is dropped. +impl Drop for CoherentAllocation { + fn drop(&mut self) { + let size = self.count * core::mem::size_of::(); + // SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`. + // The cpu address, and the dma handle are valid due to the type invariants on + // `CoherentAllocation`. + unsafe { + bindings::dma_free_attrs( + self.dev.as_raw(), + size, + self.cpu_addr as _, + self.dma_handle, + self.dma_attrs.as_raw(), + ) + } + } +} + +/// Reads a field of an item from an allocated region of structs. +/// +/// # Examples +/// +/// ``` +/// use kernel::device::Device; +/// use kernel::dma::{attrs::*, CoherentAllocation}; +/// +/// struct MyStruct { field: u32, } +/// +/// // SAFETY: All bit patterns are acceptable values for `MyStruct`. +/// unsafe impl kernel::transmute::FromBytes for MyStruct{}; +/// // SAFETY: Instances of `MyStruct` have no uninitialized portions. +/// unsafe impl kernel::transmute::AsBytes for MyStruct{}; +/// +/// # fn test(alloc: &kernel::dma::CoherentAllocation) -> Result { +/// let whole = kernel::dma_read!(alloc[2]); +/// let field = kernel::dma_read!(alloc[1].field); +/// # Ok::<(), Error>(()) } +/// ``` +#[macro_export] +macro_rules! dma_read { + ($dma:expr, $idx: expr, $($field:tt)*) => {{ + let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?; + // SAFETY: `item_from_index` ensures that `item` is always a valid pointer and can be + // dereferenced. The compiler also further validates the expression on whether `field` + // is a member of `item` when expanded by the macro. + unsafe { + let ptr_field = ::core::ptr::addr_of!((*item) $($field)*); + $crate::dma::CoherentAllocation::field_read(&$dma, ptr_field) + } + }}; + ($dma:ident [ $idx:expr ] $($field:tt)* ) => { + $crate::dma_read!($dma, $idx, $($field)*); + }; + ($($dma:ident).* [ $idx:expr ] $($field:tt)* ) => { + $crate::dma_read!($($dma).*, $idx, $($field)*); + }; +} + +/// Writes to a field of an item from an allocated region of structs. +/// +/// # Examples +/// +/// ``` +/// use kernel::device::Device; +/// use kernel::dma::{attrs::*, CoherentAllocation}; +/// +/// struct MyStruct { member: u32, } +/// +/// // SAFETY: All bit patterns are acceptable values for `MyStruct`. +/// unsafe impl kernel::transmute::FromBytes for MyStruct{}; +/// // SAFETY: Instances of `MyStruct` have no uninitialized portions. +/// unsafe impl kernel::transmute::AsBytes for MyStruct{}; +/// +/// # fn test(alloc: &kernel::dma::CoherentAllocation) -> Result { +/// kernel::dma_write!(alloc[2].member = 0xf); +/// kernel::dma_write!(alloc[1] = MyStruct { member: 0xf }); +/// # Ok::<(), Error>(()) } +/// ``` +#[macro_export] +macro_rules! dma_write { + ($dma:ident [ $idx:expr ] $($field:tt)*) => {{ + $crate::dma_write!($dma, $idx, $($field)*); + }}; + ($($dma:ident).* [ $idx:expr ] $($field:tt)* ) => {{ + $crate::dma_write!($($dma).*, $idx, $($field)*); + }}; + ($dma:expr, $idx: expr, = $val:expr) => { + let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?; + // SAFETY: `item_from_index` ensures that `item` is always a valid item. + unsafe { $crate::dma::CoherentAllocation::field_write(&$dma, item, $val) } + }; + ($dma:expr, $idx: expr, $(.$field:ident)* = $val:expr) => { + let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?; + // SAFETY: `item_from_index` ensures that `item` is always a valid pointer and can be + // dereferenced. The compiler also further validates the expression on whether `field` + // is a member of `item` when expanded by the macro. + unsafe { + let ptr_field = ::core::ptr::addr_of_mut!((*item) $(.$field)*); + $crate::dma::CoherentAllocation::field_write(&$dma, ptr_field, $val) + } + }; +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index c92497c7c655..001374a96d66 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -44,6 +44,7 @@ pub mod cred; pub mod device; pub mod device_id; pub mod devres; +pub mod dma; pub mod driver; pub mod error; pub mod faux; -- 2.51.0 From 9901addae63b9033335fb484a5f0c4367322df8b Mon Sep 17 00:00:00 2001 From: Abdiel Janulgue Date: Mon, 17 Mar 2025 20:52:10 +0200 Subject: [PATCH 15/16] samples: rust: add Rust dma test sample driver Add a simple driver to exercise the basics of the Rust DMA coherent allocator bindings. Suggested-by: Danilo Krummrich Signed-off-by: Abdiel Janulgue Acked-by: Danilo Krummrich Link: https://lore.kernel.org/r/20250317185345.2608976-4-abdiel.janulgue@gmail.com [ Renamed Kconfig symbol and moved it up. Migrated to the new `authors` key in `module!`. Fixed module name in description and typo in commit message. - Miguel ] Signed-off-by: Miguel Ojeda --- samples/rust/Kconfig | 11 +++++ samples/rust/Makefile | 1 + samples/rust/rust_dma.rs | 97 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 samples/rust/rust_dma.rs diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 3b6eae84b297..cad52b7120b5 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -40,6 +40,17 @@ config SAMPLE_RUST_PRINT If unsure, say N. +config SAMPLE_RUST_DMA + tristate "DMA Test Driver" + depends on PCI + help + This option builds the Rust DMA Test driver sample. + + To compile this as a module, choose M here: + the module will be called rust_dma. + + If unsure, say N. + config SAMPLE_RUST_DRIVER_PCI tristate "PCI Driver" depends on PCI diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 0dbc6d90f1ef..c6a2479f7d9c 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -4,6 +4,7 @@ ccflags-y += -I$(src) # needed for trace events obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) += rust_misc_device.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o +obj-$(CONFIG_SAMPLE_RUST_DMA) += rust_dma.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) += rust_driver_faux.o diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs new file mode 100644 index 000000000000..908acd34b8db --- /dev/null +++ b/samples/rust/rust_dma.rs @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust DMA api test (based on QEMU's `pci-testdev`). +//! +//! To make this driver probe, QEMU must be run with `-device pci-testdev`. + +use kernel::{bindings, dma::CoherentAllocation, pci, prelude::*}; + +struct DmaSampleDriver { + pdev: pci::Device, + ca: CoherentAllocation, +} + +const TEST_VALUES: [(u32, u32); 5] = [ + (0xa, 0xb), + (0xc, 0xd), + (0xe, 0xf), + (0xab, 0xba), + (0xcd, 0xef), +]; + +struct MyStruct { + h: u32, + b: u32, +} + +impl MyStruct { + fn new(h: u32, b: u32) -> Self { + Self { h, b } + } +} +// SAFETY: All bit patterns are acceptable values for `MyStruct`. +unsafe impl kernel::transmute::AsBytes for MyStruct {} +// SAFETY: Instances of `MyStruct` have no uninitialized portions. +unsafe impl kernel::transmute::FromBytes for MyStruct {} + +kernel::pci_device_table!( + PCI_TABLE, + MODULE_PCI_TABLE, + ::IdInfo, + [( + pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5), + () + )] +); + +impl pci::Driver for DmaSampleDriver { + type IdInfo = (); + const ID_TABLE: pci::IdTable = &PCI_TABLE; + + fn probe(pdev: &mut pci::Device, _info: &Self::IdInfo) -> Result>> { + dev_info!(pdev.as_ref(), "Probe DMA test driver.\n"); + + let ca: CoherentAllocation = + CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?; + + || -> Result { + for (i, value) in TEST_VALUES.into_iter().enumerate() { + kernel::dma_write!(ca[i] = MyStruct::new(value.0, value.1)); + } + + Ok(()) + }()?; + + let drvdata = KBox::new( + Self { + pdev: pdev.clone(), + ca, + }, + GFP_KERNEL, + )?; + + Ok(drvdata.into()) + } +} + +impl Drop for DmaSampleDriver { + fn drop(&mut self) { + dev_info!(self.pdev.as_ref(), "Unload DMA test driver.\n"); + + let _ = || -> Result { + for (i, value) in TEST_VALUES.into_iter().enumerate() { + assert_eq!(kernel::dma_read!(self.ca[i].h), value.0); + assert_eq!(kernel::dma_read!(self.ca[i].b), value.1); + } + Ok(()) + }(); + } +} + +kernel::module_pci_driver! { + type: DmaSampleDriver, + name: "rust_dma", + authors: ["Abdiel Janulgue"], + description: "Rust DMA test", + license: "GPL v2", +} -- 2.51.0 From 3ba83d37615ac66d8f52e745dedf9510e493fe97 Mon Sep 17 00:00:00 2001 From: Abdiel Janulgue Date: Mon, 17 Mar 2025 20:52:11 +0200 Subject: [PATCH 16/16] MAINTAINERS: add entry for Rust dma mapping helpers device driver API Add an entry for the Rust dma mapping helpers abstractions. Nacked-by: Christoph Hellwig Acked-by: Danilo Krummrich Acked-by: Andreas Hindborg Acked-by: Marek Szyprowski Signed-off-by: Abdiel Janulgue Link: https://lore.kernel.org/r/20250317185345.2608976-5-abdiel.janulgue@gmail.com Signed-off-by: Miguel Ojeda --- MAINTAINERS | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index af59c90e6bd6..cbf84690c495 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6902,6 +6902,19 @@ F: include/linux/dma-mapping.h F: include/linux/swiotlb.h F: kernel/dma/ +DMA MAPPING HELPERS DEVICE DRIVER API [RUST] +M: Abdiel Janulgue +M: Danilo Krummrich +R: Daniel Almeida +R: Robin Murphy +R: Andreas Hindborg +L: rust-for-linux@vger.kernel.org +S: Supported +W: https://rust-for-linux.com +T: git https://github.com/Rust-for-Linux/linux.git alloc-next +F: rust/kernel/dma.rs +F: samples/rust/rust_dma.rs + DMA-BUF HEAPS FRAMEWORK M: Sumit Semwal R: Benjamin Gaignard -- 2.51.0