diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 2d7606135ef61c..ec9d0cba1ff58b 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -21,6 +21,7 @@ #![feature(new_uninit)] #![feature(pin_macro)] #![feature(receiver_trait)] +#![feature(type_alias_impl_trait)] #![feature(unsize)] // Ensure conditional compilation based on the kernel configuration works; @@ -59,7 +60,7 @@ const __LOG_PREFIX: &[u8] = b"rust_kernel\0"; /// The top level entrypoint to implementing a kernel module. /// /// For any teardown or cleanup operations, your type may implement [`Drop`]. -pub trait Module: Sized + Sync { +pub trait Module: Sized + Sync + Send { /// Called at module initialization time. /// /// Use this method to perform whatever setup or registration your module @@ -69,6 +70,34 @@ pub trait Module: Sized + Sync { fn init(module: &'static ThisModule) -> error::Result; } +/// A module that is pinned and initialised in-place. +pub trait InPlaceModule: Sync + Send { + /// The type that implements the pinned initialisation of the module. + type Init: init::PinInit; + + /// Creates an initialiser for the module. + /// + /// It is called when the module is loaded. + fn init(module: &'static ThisModule) -> error::Result; +} + +impl InPlaceModule for T { + type Init = impl init::PinInit; + + fn init(module: &'static ThisModule) -> error::Result { + let m = ::init(module)?; + + let initer = move |slot: *mut Self| { + // SAFETY: `slot` is valid for write per the contract with `pin_init_from_closure`. + unsafe { slot.write(m) }; + Ok(()) + }; + + // SAFETY: On success, `initer` always fully initialises an instance of `Self`. + Ok(unsafe { init::pin_init_from_closure(initer) }) + } +} + /// Equivalent to `THIS_MODULE` in the C API. /// /// C header: `include/linux/export.h` diff --git a/rust/macros/module.rs b/rust/macros/module.rs index fb1244f8c2e694..0a3ed6edc2e3a6 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -208,7 +208,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { #[used] static __IS_RUST_MODULE: () = (); - static mut __MOD: Option<{type_}> = None; + static mut __MOD: core::mem::MaybeUninit<{type_}> = core::mem::MaybeUninit::uninit(); // SAFETY: `__this_module` is constructed by the kernel at load time and will not be // freed until the module is unloaded. @@ -270,23 +270,21 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { }} fn __init() -> core::ffi::c_int {{ - match <{type_} as kernel::Module>::init(&THIS_MODULE) {{ - Ok(m) => {{ - unsafe {{ - __MOD = Some(m); - }} - return 0; - }} - Err(e) => {{ - return e.to_errno(); - }} + let initer = match <{type_} as kernel::InPlaceModule>::init(&THIS_MODULE) {{ + Ok(i) => i, + Err(e) => return e.to_errno(), + }}; + + match unsafe {{ initer.__pinned_init(__MOD.as_mut_ptr()) }} {{ + Ok(m) => 0, + Err(e) => e.to_errno(), }} }} fn __exit() {{ unsafe {{ // Invokes `drop()` on `__MOD`, which should be used for cleanup. - __MOD = None; + __MOD.assume_init_drop(); }} }} diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index b0f74a81c8f9ad..59f44a8b6958ef 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -20,6 +20,17 @@ config SAMPLE_RUST_MINIMAL If unsure, say N. +config SAMPLE_RUST_INPLACE + tristate "Minimal in-place" + help + This option builds the Rust minimal module with in-place + initialisation. + + To compile this as a module, choose M here: + the module will be called rust_inplace. + + If unsure, say N. + config SAMPLE_RUST_PRINT tristate "Printing macros" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 03086dabbea44f..791fc18180e98b 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o +obj-$(CONFIG_SAMPLE_RUST_INPLACE) += rust_inplace.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs diff --git a/samples/rust/rust_inplace.rs b/samples/rust/rust_inplace.rs new file mode 100644 index 00000000000000..603e5ce10a04e9 --- /dev/null +++ b/samples/rust/rust_inplace.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust minimal in-place sample. + +use kernel::prelude::*; + +module! { + type: RustInPlace, + name: "rust_inplace", + author: "Rust for Linux Contributors", + description: "Rust minimal in-place sample", + license: "GPL", +} + +#[pin_data(PinnedDrop)] +struct RustInPlace { + numbers: Vec, +} + +impl kernel::InPlaceModule for RustInPlace { + type Init = impl PinInit; + fn init(_module: &'static ThisModule) -> Result { + pr_info!("Rust minimal sample (init)\n"); + pr_info!("Am I built-in? {}\n", !cfg!(MODULE)); + + let mut numbers = Vec::new(); + numbers.try_push(72)?; + numbers.try_push(108)?; + numbers.try_push(200)?; + + Ok(try_pin_init!(Self { numbers })) + } +} + +#[pinned_drop] +impl PinnedDrop for RustInPlace { + fn drop(self: Pin<&mut Self>) { + pr_info!("My numbers are {:?}\n", self.numbers); + pr_info!("Rust minimal inplace sample (exit)\n"); + } +} diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 9f94fc83f08652..0d8c8de68f1e6c 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -277,7 +277,7 @@ $(obj)/%.lst: $(src)/%.c FORCE # Compile Rust sources (.rs) # --------------------------------------------------------------------------- -rust_allowed_features := core_ffi_c,explicit_generic_args_with_impl_trait,new_uninit,pin_macro +rust_allowed_features := core_ffi_c,explicit_generic_args_with_impl_trait,new_uninit,pin_macro,type_alias_impl_trait rust_common_cmd = \ RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \