From f26f2f22c6d16d6019fbd1248681e57a1a533bd3 Mon Sep 17 00:00:00 2001 From: Luiz Georg Date: Fri, 10 Jul 2026 17:20:31 +0100 Subject: rust: pin-init: internal: error on duplicate `#[pin]` attribute Duplicated `#[pin]` has no effect, thus error if misused. Reported-by: Mohamad Alsadhan Closes: https://github.com/Rust-for-Linux/pin-init/issues/119 Signed-off-by: Luiz Georg Link: https://patch.msgid.link/20260710-pin-init-sync-v1-1-8fa16cde87ae@garyguo.net [ Reworded commit message, and change the logic so code generation still continue after reporting error - Gary ] Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 9fbbd25bcaac..263f67300727 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -85,7 +85,10 @@ pub(crate) fn pin_data( .map(|field| { let len = field.attrs.len(); field.attrs.retain(|a| !a.path().is_ident("pin")); - let pinned = len != field.attrs.len(); + let pinned_count = len - field.attrs.len(); + if pinned_count > 1 { + dcx.error(&field, "#[pin] attribute specified more than once"); + } let cfg_attrs = field .attrs @@ -95,7 +98,7 @@ pub(crate) fn pin_data( FieldInfo { field: &*field, - pinned, + pinned: pinned_count != 0, cfg_attrs, } }) -- cgit From 554d1afffc391f938ca58ab74b82238ac2d29c37 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 10 Jul 2026 17:20:32 +0100 Subject: rust: pin-init: examples: fix incorrect drop Remove the drop and associated clippy allow. The warning reported by Clippy here is genuine; the binding created is `Pin<&mut T>` so dropping it does nothing. `stack_pin_init` created bindings are only dropped at the end of scope. Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260710-pin-init-sync-v1-2-8fa16cde87ae@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/examples/mutex.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rust/pin-init/examples/mutex.rs b/rust/pin-init/examples/mutex.rs index 35ecb5f68dc3..882f3e23f5dd 100644 --- a/rust/pin-init/examples/mutex.rs +++ b/rust/pin-init/examples/mutex.rs @@ -91,7 +91,7 @@ impl CMutex { pub fn lock(&self) -> Pin> { let mut sguard = self.spin_lock.acquire(); if self.locked.get() { - stack_pin_init!(let wait_entry = WaitEntry::insert_new(&self.wait_list)); + stack_pin_init!(let _wait_entry = WaitEntry::insert_new(&self.wait_list)); // println!("wait list length: {}", self.wait_list.size()); while self.locked.get() { drop(sguard); @@ -99,9 +99,6 @@ impl CMutex { thread::park(); sguard = self.spin_lock.acquire(); } - // This does have an effect, as the ListHead inside wait_entry implements Drop! - #[expect(clippy::drop_non_drop)] - drop(wait_entry); } self.locked.set(true); unsafe { -- cgit From 690d82bf1da92f0a073df3728b76d4dbc99b5172 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 10 Jul 2026 17:20:33 +0100 Subject: rust: pin-init: remove redundant clippy expects in doc tests These lints are automatically suppressed inside doc tests. Previously this is needed because kernel builds doc tests with the default set of clippy flags; but now `clippy::disallowed_names` is globally allowed inside doc tests. Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260710-pin-init-sync-v1-3-8fa16cde87ae@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index fd40c8f244a1..90e9d501d44a 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -70,7 +70,6 @@ //! that you need to write `<-` instead of `:` for fields that you want to initialize in-place. //! //! ```rust -//! # #![expect(clippy::disallowed_names)] //! # #![feature(allocator_api)] //! # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; //! # use core::pin::Pin; @@ -94,7 +93,6 @@ //! (or just the stack) to actually initialize a `Foo`: //! //! ```rust -//! # #![expect(clippy::disallowed_names)] //! # #![feature(allocator_api)] //! # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; //! # use core::{alloc::AllocError, pin::Pin}; @@ -456,7 +454,6 @@ pub use ::pin_init_internal::MaybeZeroable; /// # Examples /// /// ```rust -/// # #![expect(clippy::disallowed_names)] /// # #![feature(allocator_api)] /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; /// # use pin_init::*; @@ -508,7 +505,6 @@ macro_rules! stack_pin_init { /// # Examples /// /// ```rust -/// # #![expect(clippy::disallowed_names)] /// # #![feature(allocator_api)] /// # #[path = "../examples/error.rs"] mod error; use error::Error; /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; @@ -535,7 +531,6 @@ macro_rules! stack_pin_init { /// ``` /// /// ```rust -/// # #![expect(clippy::disallowed_names)] /// # #![feature(allocator_api)] /// # #[path = "../examples/error.rs"] mod error; use error::Error; /// # #[path = "../examples/mutex.rs"] mod mutex; use mutex::*; @@ -658,7 +653,6 @@ macro_rules! stack_try_pin_init { /// Users of `Foo` can now create it like this: /// /// ```rust -/// # #![expect(clippy::disallowed_names)] /// # use pin_init::*; /// # use core::pin::Pin; /// # #[pin_data] @@ -1031,7 +1025,6 @@ pub unsafe trait Init: PinInit { /// # Examples /// /// ```rust - /// # #![expect(clippy::disallowed_names)] /// use pin_init::{init, init_zeroed, Init}; /// /// struct Foo { -- cgit From c1722ae6fefe3723f31656172f4bc196225506dc Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 10 Jul 2026 17:20:34 +0100 Subject: rust: pin-init: internal: remove `allow` and `expect`s that don't fire Most warnings are suppressed from external macro expansions by default. Thus remove `allow` and `expect`s for them. Note that `unfulfilled_lint_expectations` is one of them too. This means that all of our `expect`s inside macros do nothing, and actually mislead people to the lints would be actually emitted without them. Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260710-pin-init-sync-v1-4-8fa16cde87ae@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 2 +- rust/pin-init/internal/src/pin_data.rs | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index 28d30805d06b..c1197a994c82 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -334,7 +334,7 @@ fn make_field_check( }), }; quote! { - #[allow(unreachable_code, clippy::diverging_sub_expression)] + #[allow(unreachable_code)] // We use unreachable code to perform field checks. They're still checked by the compiler. // SAFETY: this code is never executed. let _ = || unsafe { diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 263f67300727..4438107682e0 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -245,7 +245,6 @@ fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenSt // `Drop`. Additionally we will implement this trait for the struct leading to a conflict, // if it also implements `Drop` trait MustNotImplDrop {} - #[expect(drop_bounds)] impl MustNotImplDrop for T {} impl #impl_generics MustNotImplDrop for #ident #ty_generics #whr @@ -253,7 +252,6 @@ fn generate_drop_impl(ident: &Ident, generics: &Generics, args: Args) -> TokenSt // We also take care to prevent users from writing a useless `PinnedDrop` implementation. // They might implement `PinnedDrop` correctly for the struct, but forget to give // `PinnedDrop` as the parameter to `#[pin_data]`. - #[expect(non_camel_case_types)] trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {} impl UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {} @@ -432,7 +430,6 @@ fn generate_the_pin_data( {} #[allow(dead_code)] // Some functions might never be used and private. - #[expect(clippy::missing_safety_doc)] impl #impl_generics __ThePinData #ty_generics #whr { -- cgit From 751ecd5a19cf2c55807bf30f35bafc32e179a19c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 10 Jul 2026 17:20:35 +0100 Subject: rust: pin-init: internal: generate brace in macro for init code blocks `init!` support interleaving code execution and initialization, and code execution is done using `_: { ... }` syntax. If the code inside block is a single statement, Rust may add a lint about unused braces, but the suggestion will be incorrect as block is required by pin-init. Currently we use `unused_brace` to suppress this, but this affect everything nested inside as well. Use an alternative approach by generating the block from the macro, then rustc will know to not emit the lint. Reviewed-by: Benno Lossin Link: https://patch.msgid.link/20260710-pin-init-sync-v1-5-8fa16cde87ae@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/internal/src/init.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/src/init.rs index c1197a994c82..fd0b5ea4a0a3 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -233,10 +233,12 @@ fn init_fields( InitializerKind::Value { ident, .. } => ident, InitializerKind::Init { ident, .. } => ident, InitializerKind::Code { block, .. } => { + let stmt = &block.stmts; res.extend(quote! { #(#attrs)* - #[allow(unused_braces)] - #block + { + #(#stmt)* + } }); continue; } -- cgit From 7e4d9c946de525cac36bb693c42a147b8e9f03c5 Mon Sep 17 00:00:00 2001 From: Mirko Adzic Date: Fri, 10 Jul 2026 17:20:36 +0100 Subject: rust: pin-init: make `[pin_]init_array_from_fn` unwind safe The previous code only ran cleanup on the explicit error path. If the per- element initializer panicked partway through, the elements already written into the array would be leaked: their `Drop` impls would never run. This violates the pinning requirement. Fix the unwind safety issue by adding a guard type that drops element on both error and panic path. To avoid having to duplicate code between `pin_init_array_from_fn` and the non-pin variant, extract the code to a shared `ArrayInit` type; this type is internal and not visible via API. Reported-by: Gary Guo Closes: https://github.com/Rust-for-Linux/pin-init/issues/136 Signed-off-by: Mirko Adzic Link: https://patch.msgid.link/20260710-pin-init-sync-v1-6-8fa16cde87ae@garyguo.net [ Split guard type and the initializer type, move the guard type to be within __pinned_init. - Gary ] Co-developed-by: Gary Guo Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 122 +++++++++++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 42 deletions(-) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 90e9d501d44a..3fc4a674a487 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1186,6 +1186,82 @@ pub fn uninit() -> impl Init, E> { unsafe { init_from_closure(|_| Ok(())) } } +/// Array initializer from element initializer. +struct ArrayInit(F, __internal::PhantomInvariant); + +// SAFETY: On success, all `N` elements of the array have been initialized. On error or panic, the +// elements that have been initialized so far are dropped, thus leaving the array uninitialized and +// ready to deallocate. +unsafe impl PinInit<[T; N], E> for ArrayInit +where + F: FnMut(usize) -> I, + I: PinInit, +{ + unsafe fn __pinned_init(mut self, slot: *mut [T; N]) -> Result<(), E> { + /// # Invariants + /// + /// - `ptr[..num_init]` contains initialized elements of type `T` + /// - `ptr[num_init..N]` (where N is the size of the array) contains uninitialized memory + struct ArrayInitGuard { + /// A pointer to the first element of the array. + ptr: *mut T, + /// The number of initialized elements in the array. + num_init: usize, + } + + impl Drop for ArrayInitGuard { + #[inline] + fn drop(&mut self) { + // SAFETY: Per type invariant, `self.ptr[..self.num_init]` are initialized. + unsafe { + core::ptr::drop_in_place(core::ptr::slice_from_raw_parts_mut( + self.ptr, + self.num_init, + )) + }; + } + } + + // INVARIANT: nothing is initialized yet. + let mut guard = ArrayInitGuard { + ptr: slot.cast::(), + num_init: 0, + }; + + for i in 0..N { + // INVARIANT: Elements `self.ptr[..self.num_init]` have been initialized + // thus far. This holds true for every `self.num_init = i`. + guard.num_init = i; + + let init = (self.0)(i); + // SAFETY: + // - The subslot is derived from `slot` with a valid offset. + // - If `Err` is touched, the subslot is not touched further, the guard will drop + // previously initialized elements only. + // - `slot` is pinned so is the subslot. + unsafe { init.__pinned_init(&raw mut (*slot)[i]) }?; + } + + // Dismiss the drop guard now that all elements are initialized. + core::mem::forget(guard); + Ok(()) + } +} + +// SAFETY: Follows the `PinInit` impl. `__init` executes the same code as `__pinned_init`. +unsafe impl Init<[T; N], E> for ArrayInit +where + F: FnMut(usize) -> I, + I: Init, +{ + #[inline(always)] + unsafe fn __init(self, slot: *mut [T; N]) -> Result<(), E> { + // SAFETY: `I: Init` cancels out the pinning requirement on subslots. The other safety + // requirements follow that of `__init`. + unsafe { self.__pinned_init(slot) } + } +} + /// Initializes an array by initializing each element via the provided initializer. /// /// # Examples @@ -1197,31 +1273,12 @@ pub fn uninit() -> impl Init, E> { /// assert_eq!(array.len(), 1_000); /// ``` pub fn init_array_from_fn( - mut make_init: impl FnMut(usize) -> I, + make_init: impl FnMut(usize) -> I, ) -> impl Init<[T; N], E> where I: Init, { - let init = move |slot: *mut [T; N]| { - let slot = slot.cast::(); - for i in 0..N { - let init = make_init(i); - // SAFETY: Since 0 <= `i` < N, it is still in bounds of `[T; N]`. - let ptr = unsafe { slot.add(i) }; - // SAFETY: The pointer is derived from `slot` and thus satisfies the `__init` - // requirements. - if let Err(e) = unsafe { init.__init(ptr) } { - // SAFETY: The loop has initialized the elements `slot[0..i]` and since we return - // `Err` below, `slot` will be considered uninitialized memory. - unsafe { ptr::drop_in_place(ptr::slice_from_raw_parts_mut(slot, i)) }; - return Err(e); - } - } - Ok(()) - }; - // SAFETY: The initializer above initializes every element of the array. On failure it drops - // any initialized elements and returns `Err`. - unsafe { init_from_closure(init) } + ArrayInit(make_init, __internal::PhantomInvariant::new()) } /// Initializes an array by initializing each element via the provided initializer. @@ -1240,31 +1297,12 @@ where /// assert_eq!(array.len(), 1_000); /// ``` pub fn pin_init_array_from_fn( - mut make_init: impl FnMut(usize) -> I, + make_init: impl FnMut(usize) -> I, ) -> impl PinInit<[T; N], E> where I: PinInit, { - let init = move |slot: *mut [T; N]| { - let slot = slot.cast::(); - for i in 0..N { - let init = make_init(i); - // SAFETY: Since 0 <= `i` < N, it is still in bounds of `[T; N]`. - let ptr = unsafe { slot.add(i) }; - // SAFETY: The pointer is derived from `slot` and thus satisfies the `__init` - // requirements. - if let Err(e) = unsafe { init.__pinned_init(ptr) } { - // SAFETY: The loop has initialized the elements `slot[0..i]` and since we return - // `Err` below, `slot` will be considered uninitialized memory. - unsafe { ptr::drop_in_place(ptr::slice_from_raw_parts_mut(slot, i)) }; - return Err(e); - } - } - Ok(()) - }; - // SAFETY: The initializer above initializes every element of the array. On failure it drops - // any initialized elements and returns `Err`. - unsafe { pin_init_from_closure(init) } + ArrayInit(make_init, __internal::PhantomInvariant::new()) } /// Construct an initializer in a closure and run it. -- cgit From 0c20f77a26b89bc911d31cd79f1abe1a7ae57f60 Mon Sep 17 00:00:00 2001 From: Mirko Adzic Date: Fri, 10 Jul 2026 17:20:37 +0100 Subject: rust: pin-init: make `[pin_]chain` unwind safe Add a drop guard before the call to the chained closure so that the value initialized by the first stage is dropped if the closure errors or panics; `mem::forget` the guard on success. The previous code only ran cleanup on the explicit error path, leaking the first-stage value if the chained closure panicked. Reported-by: Gary Guo Closes: https://github.com/Rust-for-Linux/pin-init/issues/136 Suggested-by: Gary Guo Signed-off-by: Mirko Adzic Link: https://patch.msgid.link/20260710-pin-init-sync-v1-7-8fa16cde87ae@garyguo.net [ Fix Clippy missing safety comment false positive when `slot` and `guard` creation are merged in a single line. - Gary ] Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 3fc4a674a487..ef9f20b11034 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -959,13 +959,11 @@ where { unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { // SAFETY: All requirements fulfilled since this function is `__pinned_init`. - unsafe { self.0.__pinned_init(slot)? }; - // SAFETY: The above call initialized `slot` and we still have unique access. - let val = unsafe { &mut *slot }; - // SAFETY: `slot` is considered pinned. - let val = unsafe { Pin::new_unchecked(val) }; - // SAFETY: `slot` was initialized above. - (self.1)(val).inspect_err(|_| unsafe { core::ptr::drop_in_place(slot) }) + let slot = unsafe { __internal::Slot::<__internal::Pinned, _>::new(slot) }; + let mut guard = slot.init(self.0)?; + (self.1)(guard.let_binding())?; + core::mem::forget(guard); + Ok(()) } } @@ -1065,11 +1063,11 @@ where { unsafe fn __init(self, slot: *mut T) -> Result<(), E> { // SAFETY: All requirements fulfilled since this function is `__init`. - unsafe { self.0.__pinned_init(slot)? }; - // SAFETY: The above call initialized `slot` and we still have unique access. - (self.1)(unsafe { &mut *slot }).inspect_err(|_| - // SAFETY: `slot` was initialized above. - unsafe { core::ptr::drop_in_place(slot) }) + let slot = unsafe { __internal::Slot::<__internal::Unpinned, _>::new(slot) }; + let mut guard = slot.init(self.0)?; + (self.1)(guard.let_binding())?; + core::mem::forget(guard); + Ok(()) } } -- cgit From 5bbf2b2deb94d0ef8324866d39cb5e0947ca5068 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 23 Jul 2026 19:19:44 +0100 Subject: rust: pin-init: internal: rework how `#[pin_data]` handles cfg Attribute macros are invoked without cfg being resolved. This adds quite a bit complexity to the macro because all of the macro needs to be careful to attach necessary cfgs. This becomes especially tricky for tuple structs. Thus, it is convenient if cfgs are all resolved like derive macros. The most optimal way to handle this is via `TokenStream::expand_expr`, but that is still unstable. We can also create an internal derive macro and transform the attribute macro invocation to be derive macro, but doing requires us to serialize all extracted information in a form of helper attributes; it would also make it more difficult if we want to make changes to the struct (which the self-reference feature would need). Implement an approach where we generate two cfg-gated macro invocations with cfg resolved within the invocation. This would mean when the loop falls through, all field cfgs are resolved, so remove all handling of cfg_attrs for the rest of the macro. Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 82 ++++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 4438107682e0..3c9d9c7364e2 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -1,13 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT use proc_macro2::TokenStream; -use quote::{format_ident, quote}; +use quote::{format_ident, quote, ToTokens}; use syn::{ parse::{End, Nothing, Parse}, parse_quote, parse_quote_spanned, spanned::Spanned, visit_mut::VisitMut, - Attribute, Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause, + Field, Fields, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause, }; use crate::diagnostics::{DiagCtxt, ErrorGuaranteed}; @@ -35,10 +35,18 @@ impl Parse for Args { } } +impl ToTokens for Args { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Nothing(_) => (), + Self::PinnedDrop(kw) => kw.to_tokens(tokens), + } + } +} + struct FieldInfo<'a> { field: &'a Field, pinned: bool, - cfg_attrs: Vec<&'a Attribute>, } pub(crate) fn pin_data( @@ -68,6 +76,55 @@ pub(crate) fn pin_data( } }; + // Handling cfg can gets very complicated, especially for tuple structs. Therefore, resolve all + // field cfgs first before continuing. + // + // We need to perform this after parsing so we can reliably detect field cfgs. + for (field_idx, field) in struct_.fields.iter_mut().enumerate() { + let cfg: Vec<_> = field + .attrs + .iter() + .filter(|a| a.path().is_ident("cfg")) + .map(|a| { + a.parse_args::() + .expect("parse as token stream cannot fail") + }) + .collect(); + + if cfg.is_empty() { + continue; + } + + field.attrs.retain(|a| !a.path().is_ident("cfg")); + let cfg_true_struct = quote!(#struct_); + + let punctuated = match &mut struct_.fields { + Fields::Named(fields) => &mut fields.named, + Fields::Unnamed(fields) => &mut fields.unnamed, + Fields::Unit => unreachable!(), + }; + *punctuated = std::mem::take(punctuated) + .into_pairs() + .enumerate() + .filter(|&(i, _)| i != field_idx) + .map(|(_, p)| p) + .collect(); + let cfg_false_struct = quote!(#struct_); + + // Resolve one field at a time until we've got no more field cfgs. + // + // This is linear time because macro invocations with false cfg will not be expanded. + return Ok(quote!( + #[cfg(all(#(#cfg,)*))] + #[::pin_init::pin_data(#args)] + #cfg_true_struct + + #[cfg(not(all(#(#cfg,)*)))] + #[::pin_init::pin_data(#args)] + #cfg_false_struct + )); + } + // The generics might contain the `Self` type. Since this macro will define a new type with the // same generics and bounds, this poses a problem: `Self` will refer to the new type as opposed // to this struct definition. Therefore we have to replace `Self` with the concrete name. @@ -90,16 +147,14 @@ pub(crate) fn pin_data( dcx.error(&field, "#[pin] attribute specified more than once"); } - let cfg_attrs = field - .attrs - .iter() - .filter(|a| a.path().is_ident("cfg")) - .collect(); + assert!( + !field.attrs.iter().any(|a| a.path().is_ident("cfg")), + "cfgs should be all resolved at this point" + ); FieldInfo { field: &*field, pinned: pinned_count != 0, - cfg_attrs, } }) .collect(); @@ -185,9 +240,7 @@ fn generate_unpin_impl( let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| { let ident = f.field.ident.as_ref().unwrap(); let ty = &f.field.ty; - let cfg_attrs = &f.cfg_attrs; quote!( - #(#cfg_attrs)* #ident: #ty ) }); @@ -280,7 +333,6 @@ fn generate_projections( .iter() .map(|field| { let Field { vis, ident, ty, .. } = &field.field; - let cfg_attrs = &field.cfg_attrs; let ident = ident .as_ref() @@ -288,11 +340,9 @@ fn generate_projections( if field.pinned { ( quote!( - #(#cfg_attrs)* #vis #ident: ::core::pin::Pin<&'__pin mut #ty>, ), quote!( - #(#cfg_attrs)* // SAFETY: this field is structurally pinned. #ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) }, ), @@ -300,11 +350,9 @@ fn generate_projections( } else { ( quote!( - #(#cfg_attrs)* #vis #ident: &'__pin mut #ty, ), quote!( - #(#cfg_attrs)* #ident: &mut #this.#ident, ), ) @@ -374,7 +422,6 @@ fn generate_the_pin_data( .iter() .map(|f| { let Field { vis, ident, ty, .. } = f.field; - let cfg_attrs = &f.cfg_attrs; let field_name = ident .as_ref() @@ -391,7 +438,6 @@ fn generate_the_pin_data( /// - `(*slot).#field_name` is properly aligned. /// - `(*slot).#field_name` points to uninitialized and exclusively accessed /// memory. - #(#cfg_attrs)* // Allow `non_snake_case` since the same warning will be emitted on // the struct definition. #[allow(non_snake_case)] -- cgit From 9e813b9abdfb626f0607b1ec94d201a2b8691f4e Mon Sep 17 00:00:00 2001 From: Nicolás Antinori Date: Thu, 23 Jul 2026 19:19:45 +0100 Subject: rust: pin-init: docs: link `Zeroable::zeroed` and `pin_init::zeroed` in documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modify the comments in the `pin_init::zeroed` and `Zeroable::zeroed` functions to cross-reference each other and make developers aware of both options. This also adapts the example code in `Zeroable::zeroed` doc comments to use that function. Suggested-by: Miguel Ojeda Link: https://lore.kernel.org/rust-for-linux/CANiq72kdCAyRUmXFcqQfkHpk1miG8Gagsn0_5U8p4WpKxv9d_g@mail.gmail.com/ Signed-off-by: Nicolás Antinori [ Fix link. - Gary ] Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index ef9f20b11034..1f5005c110cd 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1539,10 +1539,13 @@ pub unsafe trait Zeroable { /// Whenever a type implements [`Zeroable`], this function should be preferred over /// [`core::mem::zeroed()`] or using `MaybeUninit::zeroed().assume_init()`. /// + /// As const traits are not yet stable, [`pin_init::zeroed()`] can be used instead + /// when initialization is required in a `const` context. + /// /// # Examples /// /// ``` - /// use pin_init::{Zeroable, zeroed}; + /// use pin_init::Zeroable; /// /// #[derive(Zeroable)] /// struct Point { @@ -1550,7 +1553,7 @@ pub unsafe trait Zeroable { /// y: u32, /// } /// - /// let point: Point = zeroed(); + /// let point: Point = Zeroable::zeroed(); /// assert_eq!(point.x, 0); /// assert_eq!(point.y, 0); /// ``` @@ -1582,6 +1585,9 @@ pub fn init_zeroed() -> impl Init { /// Whenever a type implements [`Zeroable`], this function should be preferred over /// [`core::mem::zeroed()`] or using `MaybeUninit::zeroed().assume_init()`. /// +/// While const traits remain unstable, this function serves as the `const` version of +/// [`Zeroable::zeroed()`]. +/// /// # Examples /// /// ``` -- cgit From 6d0795b507fb1db2e6aefe533d949db3a4abf4c6 Mon Sep 17 00:00:00 2001 From: Nicolás Antinori Date: Thu, 23 Jul 2026 19:19:46 +0100 Subject: rust: pin-init: mark `pin_init::zeroed` and `Zeroable::zeroed` as `#[inline]` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `pin_init::zeroed` function is a trivial wrapper around `unsafe { core::mem::zeroed() }`, whereas `Zeroable::zeroed` is a trivial wrapper around `pin_init::zeroed`. Mark them both as `#[inline]` to avoid generating unnecessary symbols for them. Signed-off-by: Nicolás Antinori Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 1f5005c110cd..f4ccb0e87200 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1557,6 +1557,7 @@ pub unsafe trait Zeroable { /// assert_eq!(point.x, 0); /// assert_eq!(point.y, 0); /// ``` + #[inline] fn zeroed() -> Self where Self: Sized, @@ -1603,6 +1604,7 @@ pub fn init_zeroed() -> impl Init { /// assert_eq!(point.x, 0); /// assert_eq!(point.y, 0); /// ``` +#[inline] pub const fn zeroed() -> T { // SAFETY:By the type invariants of `Zeroable`, all zeroes is a valid bit pattern for `T`. unsafe { core::mem::zeroed() } -- cgit From 16861ca3508e0deac70d3bcacb6dcf435b49d9ef Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 28 Jul 2026 21:14:48 +0200 Subject: objtool/rust: add one more `noreturn` Rust function for Rust 1.99.0 Starting with Rust 1.99.0 (expected 2026-10-01), `objtool` may report: rust/kernel.o: warning: objtool: _R..._6kernel3str9parse_intaNtNtB2_7private12FromStrRadix14from_str_radix() falls through to next function _R..._6kernel3str9parse_intaNtNtB2_7private12FromStrRadix16from_u64_negated() due to calls to the `noreturn` symbol: core::num::from_ascii_bytes_radix_panic The function was renamed from `from_ascii_radix_panic` [1], which is already in the list. Thus add the new one to the list so that `objtool` knows it is actually `noreturn`. See commit 56d680dd23c3 ("objtool/rust: list `noreturn` Rust functions") for more details. Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs). Cc: Josh Poimboeuf Cc: Peter Zijlstra Link: https://github.com/rust-lang/rust/pull/159554 [1] Tested-by: Alice Ryhl Link: https://patch.msgid.link/20260728191448.349241-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- tools/objtool/check.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index f03dd59e7fca..87db9f4ed9e2 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -194,6 +194,7 @@ static bool is_rust_noreturn(const struct symbol *func) */ return str_ends_with(func->name, "_4core3num20from_str_radix_panic") || str_ends_with(func->name, "_4core3num22from_ascii_radix_panic") || + str_ends_with(func->name, "_4core3num28from_ascii_bytes_radix_panic") || str_ends_with(func->name, "_4core5sliceSp15copy_from_slice17len_mismatch_fail") || str_ends_with(func->name, "_4core6option13expect_failed") || str_ends_with(func->name, "_4core6option13unwrap_failed") || -- cgit From fd045c81ed45517b023ea95e7b80a801c48e5463 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sun, 19 Jul 2026 15:07:23 +0200 Subject: rust: rust_is_available: support testing with `bash` as `/bin/sh` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `command -v` behaves differently on `dash` vs. `bash` when faced with a file without the execute bit. Thus, for the non-executable `rustc` and `bindgen` tests, support both possible outputs that the script currently gives. This makes the test script clean on distributions like Fedora. Reviewed-by: Onur Özkan Link: https://patch.msgid.link/20260719130723.162899-1-ojeda@kernel.org [ Added custom assertion message as suggested. - Miguel ] Signed-off-by: Miguel Ojeda --- scripts/rust_is_available_test.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/rust_is_available_test.py b/scripts/rust_is_available_test.py index d6d54b7ea42a..b205d792550d 100755 --- a/scripts/rust_is_available_test.py +++ b/scripts/rust_is_available_test.py @@ -177,7 +177,13 @@ else: def test_rustc_nonexecutable(self): result = self.run_script(self.Expected.FAILURE, { "RUSTC": self.nonexecutable }) - self.assertIn(f"Running '{self.nonexecutable}' to check the Rust compiler version failed with", result.stderr) + self.assertTrue( + # `dash`. + f"Running '{self.nonexecutable}' to check the Rust compiler version failed with" in result.stderr or + # `bash`. + f"Rust compiler '{self.nonexecutable}' could not be found." in result.stderr, + f"Unexpected `stderr`:\n{result.stderr}" + ) def test_rustc_unexpected_binary(self): result = self.run_script(self.Expected.FAILURE, { "RUSTC": self.unexpected_binary }) @@ -205,7 +211,13 @@ else: def test_bindgen_nonexecutable(self): result = self.run_script(self.Expected.FAILURE, { "BINDGEN": self.nonexecutable }) - self.assertIn(f"Running '{self.nonexecutable}' to check the Rust bindings generator version failed with", result.stderr) + self.assertTrue( + # `dash`. + f"Running '{self.nonexecutable}' to check the Rust bindings generator version failed with" in result.stderr or + # `bash`. + f"Rust bindings generator '{self.nonexecutable}' could not be found." in result.stderr, + f"Unexpected `stderr`:\n{result.stderr}" + ) def test_bindgen_unexpected_binary(self): result = self.run_script(self.Expected.FAILURE, { "BINDGEN": self.unexpected_binary }) -- cgit From ec72b466dbfbd098c321688d9ccacc0458e57b58 Mon Sep 17 00:00:00 2001 From: Younes Akhouayri Date: Fri, 17 Jul 2026 13:44:06 +0200 Subject: rust: print: fix broken `_printk` Rustdoc link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Rustdoc link for `_printk` points to `include/linux/_printk.h`, which does not exist. Point it to `include/linux/printk.h`, where `_printk` is declared. Fixes: 247b365dc8dc ("rust: add `kernel` crate") Signed-off-by: Younes Akhouayri Link: https://github.com/Rust-for-Linux/linux/issues/1246 Reviewed-by: Onur Özkan Link: https://patch.msgid.link/20260717-docs-printk-rustdoc-link-v1-1-892074948f75@younes.io Signed-off-by: Miguel Ojeda --- rust/kernel/print.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs index 6fd84389a858..0d62beeedca5 100644 --- a/rust/kernel/print.rs +++ b/rust/kernel/print.rs @@ -99,7 +99,7 @@ pub mod format_strings { /// The format string must be one of the ones in [`format_strings`], and /// the module name must be null-terminated. /// -/// [`_printk`]: srctree/include/linux/_printk.h +/// [`_printk`]: srctree/include/linux/printk.h #[doc(hidden)] #[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))] pub unsafe fn call_printk( -- cgit From fe39a233ea52601d416a1d02e7f70049aae47016 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Wed, 29 Jul 2026 19:38:03 +0200 Subject: rust: kbuild: disambiguate `zerocopy` for `rusttest` Starting with Rust 1.76.0, `zerocopy` was added as an (indirect) compiler dependency [1]. In turn, this meant that the `rustc-dev` component started including a precompiled `zerocopy` crate in the sysroot. This makes `rusttest` fail because the compiler finds several candidates: error[E0464]: multiple candidates for `rmeta` dependency `zerocopy` found --> rust/kernel/prelude.rs:64:9 | 64 | pub use zerocopy::{ | ^^^^^^^^ | = note: candidate #1: .../lib/rustlib/x86_64-unknown-linux-gnu/lib/libzerocopy-dfef4cb07ca752aa.rmeta = note: candidate #2: ./rust/test/libzerocopy.rlib We cannot use `--sysroot=/dev/null` for these, thus point to the dependency explicitly. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Antoni Boucher Cc: stable@vger.kernel.org Fixes: 567621523ab7 ("rust: zerocopy: enable support in kbuild") Link: https://github.com/rust-lang/rust/pull/118546 [1] Link: https://patch.msgid.link/20260729173803.13459-1-ojeda@kernel.org [ Investigated when it started happening, reworded to add that and to follow our usual style and sent on behalf of Antoni, who found this during his work to support Rust for Linux with the GCC backend, i.e. with `rustc_codegen_gcc`. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/Makefile b/rust/Makefile index 627ed79dc6f5..fbe0accc51a3 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -350,7 +350,7 @@ rusttestlib-pin_init: $(src)/pin-init/src/lib.rs rusttestlib-macros \ rusttestlib-kernel: private rustc_target_flags = --extern ffi \ --extern build_error --extern macros --extern pin_init \ --extern bindings --extern uapi \ - --extern zerocopy --extern zerocopy_derive + --extern zerocopy=$(objtree)/$(obj)/test/libzerocopy.rlib --extern zerocopy_derive rusttestlib-kernel: $(src)/kernel/lib.rs rusttestlib-bindings rusttestlib-uapi \ rusttestlib-build_error rusttestlib-pin_init $(obj)/$(libmacros_name) \ $(obj)/bindings.o rusttestlib-zerocopy rusttestlib-zerocopy_derive FORCE -- cgit From dc01dfb37b34beeefcfe1c3055364d41a4070c7e Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sun, 19 Jul 2026 14:05:14 +0200 Subject: rust: rust_is_available: warn for `bindgen` < 0.72.1 && libclang >= 22 Starting with LLVM 22, `clang_getTypeDeclaration()` may return a forward declaration instead of the type definition. This made `bindgen` generate opaque types [1][2], which in turn made us fail with e.g. error[E0609]: no field `__bindgen_anon_1` on type `bindings::kernel_param` --> rust/kernel/module_param.rs:78:46 | 78 | let container = unsafe { &*((*param).__bindgen_anon_1.arg.cast::>()) }; | ^^^^^^^^^^^^^^^^ unknown field | = note: available field is: `_address` This was fixed in `bindgen` 0.72.1 [3]. In order to clarify what is going on and avoid confusion [4][5], add a warning to `rust_is_available.sh` about it when the versions match, similar to past warnings like the one removed in: commit ae64324ad5c1 ("rust: rust_is_available: remove warning for `bindgen` < 0.69.5 && libclang >= 19.1") In addition, even if the versions match, check if the issue appears to not reproduce with the given binaries, to avoid a warning in such a case. Finally, include tests. [ Nathan, in parallel, updated the instructions of the LLVM+Rust kernel.org toolchains [6] so that `--version` is not passed to `cargo` for `bindgen`, and thus the latest `bindgen` is installed by default, which should help to avoid some of these situations. Thanks! - Miguel ] Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs). Link: https://github.com/rust-lang/rust-bindgen/issues/3264 [1] Link: https://github.com/Rust-for-Linux/linux/issues/353 [2] # "Missing fields in nested class with LLVM 22." Link: https://github.com/rust-lang/rust-bindgen/pull/3278 [3] Reported-by: Burak Emir Link: https://github.com/Rust-for-Linux/linux/issues/1247 [4] Link: https://lore.kernel.org/rust-for-linux/CABwQupNfMAJOGqRM9ke6tj4f53dCCsBDKU7Vp+zf8mwk7bqt8Q@mail.gmail.com/ [5] Link: https://mirrors.edge.kernel.org/pub/tools/llvm/rust/ [6] Tested-by: Burak Emir Link: https://patch.msgid.link/20260719120514.159914-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- scripts/rust_is_available.sh | 14 ++++++++++++ scripts/rust_is_available_bindgen_libclang_22.h | 5 +++++ scripts/rust_is_available_test.py | 30 ++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 scripts/rust_is_available_bindgen_libclang_22.h diff --git a/scripts/rust_is_available.sh b/scripts/rust_is_available.sh index 551f1ebd0dcb..c30983562a2f 100755 --- a/scripts/rust_is_available.sh +++ b/scripts/rust_is_available.sh @@ -208,6 +208,20 @@ if [ "$bindgen_libclang_cversion" -lt "$bindgen_libclang_min_cversion" ]; then exit 1 fi +if [ "$bindgen_libclang_cversion" -ge 2200000 ] && + [ "$rust_bindings_generator_cversion" -lt 7201 ]; then + # Distributions may have patched the issue. + if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang_22.h | grep -q 'pub foo'; then + echo >&2 "***" + echo >&2 "*** Rust bindings generator '$BINDGEN' < 0.72.1 together with libclang >= 22" + echo >&2 "*** may not work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/3278)." + echo >&2 "*** Your bindgen version: $rust_bindings_generator_version" + echo >&2 "*** Your libclang version: $bindgen_libclang_version" + echo >&2 "***" + warning=1 + fi +fi + # If the C compiler is Clang, then we can also check whether its version # matches the `libclang` version used by the Rust bindings generator. # diff --git a/scripts/rust_is_available_bindgen_libclang_22.h b/scripts/rust_is_available_bindgen_libclang_22.h new file mode 100644 index 000000000000..6b33544c14a8 --- /dev/null +++ b/scripts/rust_is_available_bindgen_libclang_22.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +struct S; +struct S { + int foo; +}; diff --git a/scripts/rust_is_available_test.py b/scripts/rust_is_available_test.py index b205d792550d..22bdff980c35 100755 --- a/scripts/rust_is_available_test.py +++ b/scripts/rust_is_available_test.py @@ -54,16 +54,23 @@ else: """) @classmethod - def generate_bindgen(cls, version_stdout, libclang_stderr): + def generate_bindgen(cls, version_stdout, libclang_stderr, libclang_22_patched=False): if libclang_stderr is None: libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})" else: libclang_case = f"print({repr(libclang_stderr)}, file=sys.stderr)" + if libclang_22_patched: + libclang_22_case = "print('pub foo: ::std::os::raw::c_int,')" + else: + libclang_22_case = "pass" + return cls.generate_executable(f"""#!/usr/bin/env python3 import sys if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv): {libclang_case} +elif "rust_is_available_bindgen_libclang_22.h" in " ".join(sys.argv): + {libclang_22_case} else: print({repr(version_stdout)}) """) @@ -260,6 +267,27 @@ else: result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen }) self.assertIn(f"libclang (used by the Rust bindings generator '{bindgen}') is too old.", result.stderr) + def test_bindgen_bad_libclang_22(self): + for (bindgen_version, libclang_version, expected_not_patched) in ( + ("0.71.1", "21.1.0", self.Expected.SUCCESS), + ("0.71.1", "22.0.0", self.Expected.SUCCESS_WITH_WARNINGS), + ("0.71.1", "22.1.0", self.Expected.SUCCESS_WITH_WARNINGS), + + ("0.72.0", "22.0.0", self.Expected.SUCCESS_WITH_WARNINGS), + + ("0.72.1", "22.0.0", self.Expected.SUCCESS), + ): + with self.subTest(bindgen_version=bindgen_version, libclang_version=libclang_version): + cc = self.generate_clang(f"clang version {libclang_version}") + libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {libclang_version} [-W#pragma-messages], err: false" + bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr) + result = self.run_script(expected_not_patched, { "BINDGEN": bindgen, "CC": cc }) + if expected_not_patched == self.Expected.SUCCESS_WITH_WARNINGS: + self.assertIn(f"Rust bindings generator '{bindgen}' < 0.72.1 together with libclang >= 22", result.stderr) + + bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_22_patched=True) + result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc }) + def test_clang_matches_bindgen_libclang_different_bindgen(self): bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version 999.0.0 [-W#pragma-messages], err: false") result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen }) -- cgit From c998b661b7c024dfd6dd893927506e32ee8a42c5 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 29 Jul 2026 16:38:43 +0100 Subject: rust: pin-init: examples: use `Wrapper::pin_init` instead of manual reimplementation `UnsafeCell` gains the method via the extension trait `Wrapper`. Link: https://patch.msgid.link/20260729-merge-init-v2-1-26adf47109e7@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/examples/mutex.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rust/pin-init/examples/mutex.rs b/rust/pin-init/examples/mutex.rs index 882f3e23f5dd..e8d4dbb664fe 100644 --- a/rust/pin-init/examples/mutex.rs +++ b/rust/pin-init/examples/mutex.rs @@ -79,11 +79,7 @@ impl CMutex { wait_list <- ListHead::new(), spin_lock: SpinLock::new(), locked: Cell::new(false), - data <- unsafe { - pin_init_from_closure(|slot: *mut UnsafeCell| { - val.__pinned_init(slot.cast::()) - }) - }, + data <- UnsafeCell::pin_init(val), }) } -- cgit From 91665820d9bf511e0c3fdf3edb464ba23130dafe Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 29 Jul 2026 16:38:44 +0100 Subject: rust: pin-init: merge `__pinned_init` and `__init` These functions have the same requirements and are also required to execute the same code. Prevent duplication by merging them to the single function and document the additional relaxation of `Init::__init` on both the merged function and the safety requirement of `Init`. The existing `__pinned_init` function is deprecated and kept for compatibility for existing users. For `cfg(kernel)`, it is soft-deprecated for now and will be removed when all users are migrated. Link: https://patch.msgid.link/20260729-merge-init-v2-2-26adf47109e7@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/examples/static_init.rs | 9 +-- rust/pin-init/src/__internal.rs | 8 +- rust/pin-init/src/alloc.rs | 6 +- rust/pin-init/src/lib.rs | 146 +++++++++++++--------------------- 4 files changed, 67 insertions(+), 102 deletions(-) diff --git a/rust/pin-init/examples/static_init.rs b/rust/pin-init/examples/static_init.rs index 58cd4241b78c..8e71556ffe85 100644 --- a/rust/pin-init/examples/static_init.rs +++ b/rust/pin-init/examples/static_init.rs @@ -59,7 +59,7 @@ impl> ops::Deref for StaticInit { println!("doing init"); let ptr = self.cell.get().cast::(); match self.init.take() { - Some(f) => unsafe { f.__pinned_init(ptr).unwrap() }, + Some(f) => unsafe { f.__init(ptr).unwrap() }, None => unsafe { core::hint::unreachable_unchecked() }, } self.present.set(true); @@ -71,13 +71,10 @@ impl> ops::Deref for StaticInit { pub struct CountInit; unsafe impl PinInit> for CountInit { - unsafe fn __pinned_init( - self, - slot: *mut CMutex, - ) -> Result<(), core::convert::Infallible> { + unsafe fn __init(self, slot: *mut CMutex) -> Result<(), core::convert::Infallible> { let init = CMutex::new(0); std::thread::sleep(std::time::Duration::from_millis(1000)); - unsafe { init.__pinned_init(slot) } + unsafe { init.__init(slot) } } } diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index 56dc655e323e..ae9a0e68cd75 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -181,7 +181,7 @@ impl StackInit { unsafe { this.value.assume_init_drop() }; } // SAFETY: The memory slot is valid and this type ensures that it will stay pinned. - unsafe { init.__pinned_init(this.value.as_mut_ptr())? }; + unsafe { init.__init(this.value.as_mut_ptr())? }; // INVARIANT: `this.value` is initialized above. this.is_init = true; // SAFETY: The slot is now pinned, since we will never give access to `&mut T`. @@ -289,7 +289,7 @@ impl Slot { // - when `Err` is returned, we also propagate the error without touching `ptr`; // also `self` is consumed so it cannot be touched further. // - the drop guard will not hand out `&mut` (only `Pin<&mut T>`). - unsafe { init.__pinned_init(self.ptr)? }; + unsafe { init.__init(self.ptr)? }; // SAFETY: // - `self.ptr` is valid, properly aligned and pinned per type invariant. @@ -396,9 +396,9 @@ impl Default for AlwaysFail { } } -// SAFETY: `__pinned_init` always fails, which is always okay. +// SAFETY: `__init` always fails, which is always okay. unsafe impl PinInit for AlwaysFail { - unsafe fn __pinned_init(self, _slot: *mut T) -> Result<(), ()> { + unsafe fn __init(self, _slot: *mut T) -> Result<(), ()> { Err(()) } } diff --git a/rust/pin-init/src/alloc.rs b/rust/pin-init/src/alloc.rs index 5017f57442d8..641f4c7ce890 100644 --- a/rust/pin-init/src/alloc.rs +++ b/rust/pin-init/src/alloc.rs @@ -38,7 +38,7 @@ pub trait InPlaceInit: Sized { 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) { + pin_init_from_closure(|slot| match init.__init(slot) { Ok(()) => Ok(()), Err(i) => match i {}, }) @@ -109,7 +109,7 @@ impl InPlaceInit for Arc { 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)? }; + unsafe { init.__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()) }) } @@ -149,7 +149,7 @@ impl InPlaceWrite for Box> { 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)? }; + unsafe { init.__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 f4ccb0e87200..fde53473763f 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -889,7 +889,7 @@ macro_rules! assert_pinned { /// When implementing this trait you will need to take great care. Also there are probably very few /// cases where a manual implementation is necessary. Use [`pin_init_from_closure`] where possible. /// -/// The [`PinInit::__pinned_init`] function: +/// The [`PinInit::__init`] function: /// - returns `Ok(())` if it initialized every field of `slot`, /// - returns `Err(err)` if it encountered an error and then cleaned `slot`, this means: /// - `slot` can be deallocated without UB occurring, @@ -909,6 +909,20 @@ macro_rules! assert_pinned { #[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 { + /// Alias of [`PinInit::__init`]. + /// + /// New code should use `__init` instead. + /// + /// # Safety + /// + /// Same as `__init`. + #[inline(always)] + #[cfg_attr(not(kernel), deprecated = "use `__init` instead")] + unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { + // SAFETY: Per safety requirement. + unsafe { self.__init(slot) } + } + /// Initializes `slot`. /// /// # Safety @@ -917,7 +931,8 @@ pub unsafe trait PinInit: Sized { /// - the caller does not touch `slot` when `Err` is returned, they are only permitted to /// deallocate. /// - `slot` will not move until it is dropped, i.e. it will be pinned. - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E>; + /// If `Self: Init`, this requirement is cancelled and it may be moved. + unsafe fn __init(self, slot: *mut T) -> Result<(), E>; /// First initializes the value using `self` then calls the function `f` with the initialized /// value. @@ -948,7 +963,7 @@ pub unsafe trait PinInit: Sized { /// An initializer returned by [`PinInit::pin_chain`]. pub struct ChainPinInit(I, F, __internal::PhantomInvariant<(E, T)>); -// SAFETY: The `__pinned_init` function is implemented such that it +// SAFETY: The `__init` function is implemented such that it // - returns `Ok(())` on successful initialization, // - returns `Err(err)` on error and in this case `slot` will be dropped. // - considers `slot` pinned. @@ -957,8 +972,8 @@ where I: PinInit, F: FnOnce(Pin<&mut T>) -> Result<(), E>, { - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { - // SAFETY: All requirements fulfilled since this function is `__pinned_init`. + unsafe fn __init(self, slot: *mut T) -> Result<(), E> { + // SAFETY: All requirements fulfilled since this function is `__init`. let slot = unsafe { __internal::Slot::<__internal::Pinned, _>::new(slot) }; let mut guard = slot.init(self.0)?; (self.1)(guard.let_binding())?; @@ -980,19 +995,8 @@ where /// When implementing this trait you will need to take great care. Also there are probably very few /// cases where a manual implementation is necessary. Use [`init_from_closure`] where possible. /// -/// The [`Init::__init`] function: -/// - returns `Ok(())` if it initialized every field of `slot`, -/// - returns `Err(err)` if it encountered an error and then cleaned `slot`, this means: -/// - `slot` can be deallocated without UB occurring, -/// - `slot` does not need to be dropped, -/// - `slot` is not partially initialized. -/// - while constructing the `T` at `slot` it upholds the pinning invariants of `T`. -/// -/// The `__pinned_init` function from the supertrait [`PinInit`] needs to execute the exact same -/// code as `__init`. -/// -/// Contrary to its supertype [`PinInit`] the caller is allowed to -/// move the pointee after initialization. +/// The [`PinInit::__init`] function must work without the pinning requirement; the caller is +/// allowed to move the pointee after initialization. /// #[cfg_attr( kernel, @@ -1006,15 +1010,6 @@ where #[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`. - /// - /// # Safety - /// - /// - `slot` is a valid pointer to uninitialized memory. - /// - the caller does not touch `slot` when `Err` is returned, they are only permitted to - /// deallocate. - unsafe fn __init(self, slot: *mut T) -> Result<(), E>; - /// First initializes the value using `self` then calls the function `f` with the initialized /// value. /// @@ -1053,10 +1048,18 @@ pub unsafe trait Init: PinInit { /// An initializer returned by [`Init::chain`]. pub struct ChainInit(I, F, __internal::PhantomInvariant<(E, T)>); +// SAFETY: The `__init` function does not rely on the pinning requirement. +unsafe impl Init for ChainInit +where + I: Init, + F: FnOnce(&mut T) -> Result<(), E>, +{ +} + // SAFETY: The `__init` function is implemented such that it // - returns `Ok(())` on successful initialization, // - returns `Err(err)` on error and in this case `slot` will be dropped. -unsafe impl Init for ChainInit +unsafe impl PinInit for ChainInit where I: Init, F: FnOnce(&mut T) -> Result<(), E>, @@ -1071,44 +1074,28 @@ where } } -// SAFETY: `__pinned_init` behaves exactly the same as `__init`. -unsafe impl PinInit for ChainInit -where - I: Init, - F: FnOnce(&mut T) -> Result<(), E>, -{ - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { - // SAFETY: `__init` has less strict requirements compared to `__pinned_init`. - unsafe { self.__init(slot) } - } -} - /// Implement `PinInit` and `Init` for closures. /// /// It is unsafe to create this type, since the closure needs to fulfill the same safety -/// requirement as the `__pinned_init`/`__init` functions. +/// requirement as the `__init` functions. struct InitClosure(F, __internal::PhantomInvariant); -// SAFETY: While constructing the `InitClosure`, the user promised that it upholds the -// `__init` invariants. -unsafe impl Init for InitClosure -where - F: FnOnce(*mut T) -> Result<(), E>, +// SAFETY: When constructing via `init_from_closure`, the `__init` function does not rely on the +// pinning requirement. When constructing via `pin_init_from_closure`, the opaque type prevents this +// implementation from being visible. +unsafe impl Init for InitClosure where + F: FnOnce(*mut T) -> Result<(), E> { - #[inline] - unsafe fn __init(self, slot: *mut T) -> Result<(), E> { - (self.0)(slot) - } } // SAFETY: While constructing the `InitClosure`, the user promised that it upholds the -// `__pinned_init` invariants. +// `__init` invariants. unsafe impl PinInit for InitClosure where F: FnOnce(*mut T) -> Result<(), E>, { #[inline] - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { + unsafe fn __init(self, slot: *mut T) -> Result<(), E> { (self.0)(slot) } } @@ -1160,7 +1147,7 @@ pub const unsafe fn init_from_closure( pub const unsafe fn cast_pin_init(init: impl PinInit) -> impl PinInit { // SAFETY: initialization delegated to a valid initializer. Cast is valid by function safety // requirements. - unsafe { pin_init_from_closure(|ptr: *mut U| init.__pinned_init(ptr.cast::())) } + unsafe { pin_init_from_closure(|ptr: *mut U| init.__init(ptr.cast::())) } } /// Changes the to be initialized type. @@ -1195,7 +1182,7 @@ where F: FnMut(usize) -> I, I: PinInit, { - unsafe fn __pinned_init(mut self, slot: *mut [T; N]) -> Result<(), E> { + unsafe fn __init(mut self, slot: *mut [T; N]) -> Result<(), E> { /// # Invariants /// /// - `ptr[..num_init]` contains initialized elements of type `T` @@ -1237,7 +1224,7 @@ where // - If `Err` is touched, the subslot is not touched further, the guard will drop // previously initialized elements only. // - `slot` is pinned so is the subslot. - unsafe { init.__pinned_init(&raw mut (*slot)[i]) }?; + unsafe { init.__init(&raw mut (*slot)[i]) }?; } // Dismiss the drop guard now that all elements are initialized. @@ -1246,18 +1233,13 @@ where } } -// SAFETY: Follows the `PinInit` impl. `__init` executes the same code as `__pinned_init`. +// SAFETY: `I: Init` cancels out the pinning requirement on subslots, which is the only place in the +// `__init` function that relies on `slot` being pinned. unsafe impl Init<[T; N], E> for ArrayInit where F: FnMut(usize) -> I, I: Init, { - #[inline(always)] - unsafe fn __init(self, slot: *mut [T; N]) -> Result<(), E> { - // SAFETY: `I: Init` cancels out the pinning requirement on subslots. The other safety - // requirements follow that of `__init`. - unsafe { self.__pinned_init(slot) } - } } /// Initializes an array by initializing each element via the provided initializer. @@ -1336,13 +1318,13 @@ where { // SAFETY: // - If `make_init` returns `Err`, `Err` is returned and `slot` is completely uninitialized, - // - If `make_init` returns `Ok`, safety requirement are fulfilled by `init.__pinned_init`. - // - The safety requirements of `init.__pinned_init` are fulfilled, since it's being called - // from an initializer. + // - If `make_init` returns `Ok`, safety requirement are fulfilled by `init.__init`. + // - The safety requirements of `init.__init` are fulfilled, since it's being called from an + // initializer. unsafe { pin_init_from_closure(move |slot: *mut T| -> Result<(), E> { let init = make_init()?; - init.__pinned_init(slot) + init.__init(slot) }) } } @@ -1390,41 +1372,27 @@ where } } -// SAFETY: the `__init` function always returns `Ok(())` and initializes every field of `slot`. -unsafe impl Init for T { - unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> { - // SAFETY: `slot` is valid for writes by the safety requirements of this function. - unsafe { slot.write(self) }; - Ok(()) - } -} +// SAFETY: The `__init` function does not rely on slot being pinned after it returns. +unsafe impl Init for T {} -// SAFETY: the `__pinned_init` function always returns `Ok(())` and initializes every field of +// SAFETY: the `__init` function always returns `Ok(())` and initializes every field of // `slot`. Additionally, all pinning invariants of `T` are upheld. unsafe impl PinInit for T { - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), Infallible> { + unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> { // SAFETY: `slot` is valid for writes by the safety requirements of this function. unsafe { slot.write(self) }; Ok(()) } } -// SAFETY: when the `__init` function returns with -// - `Ok(())`, `slot` was initialized and all pinned invariants of `T` are upheld. -// - `Err(err)`, slot was not written to. -unsafe impl Init for Result { - unsafe fn __init(self, slot: *mut T) -> Result<(), E> { - // SAFETY: `slot` is valid for writes by the safety requirements of this function. - unsafe { slot.write(self?) }; - Ok(()) - } -} +// SAFETY: The `__init` function does not rely on slot being pinned after it returns. +unsafe impl Init for Result {} -// SAFETY: when the `__pinned_init` function returns with +// SAFETY: when the `__init` function returns with // - `Ok(())`, `slot` was initialized and all pinned invariants of `T` are upheld. // - `Err(err)`, slot was not written to. unsafe impl PinInit for Result { - unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { + unsafe fn __init(self, slot: *mut T) -> Result<(), E> { // SAFETY: `slot` is valid for writes by the safety requirements of this function. unsafe { slot.write(self?) }; Ok(()) @@ -1467,7 +1435,7 @@ impl InPlaceWrite for &'static mut MaybeUninit { // // The `'static` borrow guarantees the data will not be // moved/invalidated until it gets dropped (which is never). - unsafe { init.__pinned_init(slot)? }; + unsafe { init.__init(slot)? }; // SAFETY: The above call initialized the memory. Ok(Pin::static_mut(unsafe { self.assume_init_mut() })) -- cgit From d5492db2bf30b7e617e41d4fe3dba22475a1a354 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 29 Jul 2026 16:38:45 +0100 Subject: rust: pin-init: add `raw_init` and `raw_try_init` and recommend over `__init` The `__init` method is not designed to be a public API (existence of "__" is a hint for this); but currently there is no other API that allows raw initialization on pointers. Add `raw_init` and `raw_try_init` and recommend people to use this instead if raw pointer initialization is needed. Link: https://patch.msgid.link/20260729-merge-init-v2-3-26adf47109e7@garyguo.net [ Renamed from `ptr_[try_]init` to `raw_[try_]init`. - Gary ] Reviewed-by: Benno Lossin Signed-off-by: Gary Guo --- rust/pin-init/examples/static_init.rs | 5 +++-- rust/pin-init/src/lib.rs | 32 +++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/rust/pin-init/examples/static_init.rs b/rust/pin-init/examples/static_init.rs index 8e71556ffe85..8dd52313c1b8 100644 --- a/rust/pin-init/examples/static_init.rs +++ b/rust/pin-init/examples/static_init.rs @@ -59,7 +59,7 @@ impl> ops::Deref for StaticInit { println!("doing init"); let ptr = self.cell.get().cast::(); match self.init.take() { - Some(f) => unsafe { f.__init(ptr).unwrap() }, + Some(f) => unsafe { pin_init::raw_init(ptr, f) }, None => unsafe { core::hint::unreachable_unchecked() }, } self.present.set(true); @@ -74,7 +74,8 @@ unsafe impl PinInit> for CountInit { unsafe fn __init(self, slot: *mut CMutex) -> Result<(), core::convert::Infallible> { let init = CMutex::new(0); std::thread::sleep(std::time::Duration::from_millis(1000)); - unsafe { init.__init(slot) } + unsafe { pin_init::raw_init(slot, init) }; + Ok(()) } } diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index fde53473763f..97eaef6f2958 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -917,7 +917,7 @@ pub unsafe trait PinInit: Sized { /// /// Same as `__init`. #[inline(always)] - #[cfg_attr(not(kernel), deprecated = "use `__init` instead")] + #[cfg_attr(not(kernel), deprecated = "use `raw_try_init` instead")] unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { // SAFETY: Per safety requirement. unsafe { self.__init(slot) } @@ -925,6 +925,8 @@ pub unsafe trait PinInit: Sized { /// Initializes `slot`. /// + /// It is not recommended to call this directly. Use [`raw_init`] or [`raw_try_init`]. + /// /// # Safety /// /// - `slot` is a valid pointer to uninitialized memory. @@ -960,6 +962,34 @@ pub unsafe trait PinInit: Sized { } } +/// Initializes `slot` with an initializer. +/// +/// # Safety +/// +/// - `slot` is a valid pointer to uninitialized memory. +/// - `slot` will not move until it is dropped, i.e. it will be pinned. +/// If `init` implements `Init`, this requirement is cancelled and it may be moved. +#[inline(always)] +pub unsafe fn raw_init(slot: *mut T, init: impl PinInit) { + // SAFETY: Per safety requirement. + unsafe { init.__init(slot).unwrap_or_else(|e| match e {}) } +} + +/// Fallibly initializes `slot` with an initializer. +/// +/// # Safety +/// +/// - `slot` is a valid pointer to uninitialized memory. +/// - the caller does not touch `slot` when `Err` is returned, they are only permitted to +/// deallocate. +/// - `slot` will not move until it is dropped, i.e. it will be pinned. +/// If `init` implements `Init`, this requirement is cancelled and it may be moved. +#[inline(always)] +pub unsafe fn raw_try_init(slot: *mut T, init: impl PinInit) -> Result<(), E> { + // SAFETY: Per safety requirement. + unsafe { init.__init(slot) } +} + /// An initializer returned by [`PinInit::pin_chain`]. pub struct ChainPinInit(I, F, __internal::PhantomInvariant<(E, T)>); -- cgit From ea7da3116015de801840f3e011dee907fb118f0c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 29 Jul 2026 16:38:46 +0100 Subject: rust: treewide: replace `__pinned_init` with `raw_[try_]init` The `__init` method is not designed to be a public API (existence of "__" is a hint for this); replace users with `pin_init::raw_[try_]init` which does the same thing. There are a few users of `__init` which are replaced as well. Acked-by: Miguel Ojeda Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260729-merge-init-v2-4-26adf47109e7@garyguo.net Signed-off-by: Gary Guo --- drivers/gpu/nova-core/gsp/cmdq.rs | 4 ++-- rust/kernel/alloc/kbox.rs | 8 ++++---- rust/kernel/dma.rs | 10 +++++----- rust/kernel/drm/device.rs | 2 +- rust/kernel/drm/gpuvm/va.rs | 2 +- rust/kernel/drm/gpuvm/vm_bo.rs | 2 +- rust/kernel/init.rs | 6 ++++-- rust/kernel/pwm.rs | 2 +- rust/kernel/sync/arc.rs | 8 ++++---- rust/kernel/types.rs | 8 ++++---- rust/macros/module.rs | 2 +- 11 files changed, 28 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs index 070de0731e95..3c68a66770d3 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -645,8 +645,8 @@ impl CmdqInner { // SAFETY: `msg_header` and `cmd` are valid references, and not touched if the initializer // fails. unsafe { - msg_element.__init(core::ptr::from_mut(dst.header))?; - command.init().__init(core::ptr::from_mut(cmd))?; + pin_init::raw_try_init(core::ptr::from_mut(dst.header), msg_element)?; + pin_init::raw_try_init(core::ptr::from_mut(cmd), command.init())?; } // Fill the variable-length payload, which may be empty. diff --git a/rust/kernel/alloc/kbox.rs b/rust/kernel/alloc/kbox.rs index 35d1e015848d..c63d6acdbb6f 100644 --- a/rust/kernel/alloc/kbox.rs +++ b/rust/kernel/alloc/kbox.rs @@ -372,13 +372,13 @@ where // - `ptr` is a valid pointer to uninitialized memory. // - `ptr` is not used if an error is returned. // - `ptr` won't be moved until it is dropped, i.e. it is pinned. - unsafe { init(i).__pinned_init(ptr)? }; + unsafe { pin_init::raw_try_init(ptr, init(i))? }; // SAFETY: // - `i + 1 <= len`, hence we don't exceed the capacity, due to the call to // `with_capacity()` above. // - The new value at index buffer.len() + 1 is the only element being added here, and - // it has been initialized above by `init(i).__pinned_init(ptr)`. + // it has been initialized above by `raw_try_init(ptr, i)`. unsafe { buffer.inc_len(1) }; } @@ -463,7 +463,7 @@ where 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)? }; + unsafe { pin_init::raw_try_init(slot, init)? }; // SAFETY: All fields have been initialized. Ok(unsafe { Box::assume_init(self) }) } @@ -473,7 +473,7 @@ where 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)? }; + unsafe { pin_init::raw_try_init(slot, init)? }; // SAFETY: All fields have been initialized. Ok(unsafe { Box::assume_init(self) }.into()) } diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs index 200def84fb69..8e36a4e7f514 100644 --- a/rust/kernel/dma.rs +++ b/rust/kernel/dma.rs @@ -449,7 +449,7 @@ impl CoherentBox<[T]> { // - `T: AsBytes + FromBytes` guarantees all bit patterns are valid, so partial writes on // error cannot leave the element in an invalid state. // - The DMA address has not been exposed yet, so there is no concurrent device access. - unsafe { init.__init(ptr)? }; + unsafe { pin_init::raw_try_init(ptr, init)? }; Ok(()) } @@ -791,10 +791,10 @@ impl Coherent { // SAFETY: // - `ptr` is valid, properly aligned, and points to exclusively owned memory. - // - If `__init` fails, `self` is dropped, which safely frees the underlying `Coherent`'s - // DMA memory. `T: AsBytes + FromBytes` ensures there are no complex `Drop` requirements - // we are bypassing. - unsafe { init.__init(ptr)? }; + // - If `raw_try_init` fails, `self` is dropped, which safely frees the underlying + // `Coherent`'s DMA memory. `T: AsBytes + FromBytes` ensures there are no complex `Drop` + // requirements we are bypassing. + unsafe { pin_init::raw_try_init(ptr, init)? }; Ok(dmem) } diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 477cf771fb10..48d8b26282d1 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -244,7 +244,7 @@ impl UnregisteredDevice { // SAFETY: // - `raw_data` is a valid pointer to uninitialized memory. // - `raw_data` will not move until it is dropped. - unsafe { data.__pinned_init(raw_data) }.inspect_err(|_| { + unsafe { pin_init::raw_try_init(raw_data, data) }.inspect_err(|_| { // SAFETY: `__drm_dev_alloc()` was successful, hence `drm_dev` must be valid and the // refcount must be non-zero. unsafe { bindings::drm_dev_put(drm_dev) }; diff --git a/rust/kernel/drm/gpuvm/va.rs b/rust/kernel/drm/gpuvm/va.rs index 0b09fe44ab39..bf927b8e6fbb 100644 --- a/rust/kernel/drm/gpuvm/va.rs +++ b/rust/kernel/drm/gpuvm/va.rs @@ -116,7 +116,7 @@ impl GpuVaAlloc { pub(super) fn prepare(mut self, va_data: impl PinInit) -> *mut bindings::drm_gpuva { let va_ptr = MaybeUninit::as_mut_ptr(&mut self.0); // SAFETY: The `data` field is pinned. - let Ok(()) = unsafe { va_data.__pinned_init(&raw mut (*va_ptr).data) }; + unsafe { pin_init::raw_init(&raw mut (*va_ptr).data, va_data) }; KBox::into_raw(self.0).cast() } } diff --git a/rust/kernel/drm/gpuvm/vm_bo.rs b/rust/kernel/drm/gpuvm/vm_bo.rs index c064ac63897b..ab12b710267e 100644 --- a/rust/kernel/drm/gpuvm/vm_bo.rs +++ b/rust/kernel/drm/gpuvm/vm_bo.rs @@ -181,7 +181,7 @@ impl GpuVmBoAlloc { }; let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; // SAFETY: `ptr->data` is a valid pinned location. - let Ok(()) = unsafe { value.__pinned_init(&raw mut (*raw_ptr).data) }; + unsafe { pin_init::raw_init(&raw mut (*raw_ptr).data, value) }; // INVARIANTS: We just created the vm_bo so it's absent from lists, and the data is valid // as we just initialized it. Ok(GpuVmBoAlloc(ptr)) diff --git a/rust/kernel/init.rs b/rust/kernel/init.rs index 05a12e869a57..1fdc3963e3e3 100644 --- a/rust/kernel/init.rs +++ b/rust/kernel/init.rs @@ -158,7 +158,9 @@ pub trait InPlaceInit: Sized { { // SAFETY: We delegate to `init` and only change the error type. let init = unsafe { - pin_init_from_closure(|slot| init.__pinned_init(slot).map_err(|e| Error::from(e))) + pin_init_from_closure(|slot| { + pin_init::raw_try_init(slot, init).map_err(|e| Error::from(e)) + }) }; Self::try_pin_init(init, flags) } @@ -176,7 +178,7 @@ pub trait InPlaceInit: Sized { { // SAFETY: We delegate to `init` and only change the error type. let init = unsafe { - init_from_closure(|slot| init.__pinned_init(slot).map_err(|e| Error::from(e))) + init_from_closure(|slot| pin_init::raw_try_init(slot, init).map_err(|e| Error::from(e))) }; Self::try_init(init, flags) } diff --git a/rust/kernel/pwm.rs b/rust/kernel/pwm.rs index 6c9d667009ef..8b3a580b4f0f 100644 --- a/rust/kernel/pwm.rs +++ b/rust/kernel/pwm.rs @@ -600,7 +600,7 @@ impl Chip { let drvdata_ptr = unsafe { bindings::pwmchip_get_drvdata(c_chip_ptr) }; // SAFETY: We construct the `T` object in-place in the allocated private memory. - unsafe { data.__pinned_init(drvdata_ptr.cast()) }.inspect_err(|_| { + unsafe { pin_init::raw_try_init(drvdata_ptr.cast(), data) }.inspect_err(|_| { // SAFETY: It is safe to call `pwmchip_put()` with a valid pointer obtained // from `pwmchip_alloc()`. We will not use pointer after this. unsafe { bindings::pwmchip_put(c_chip_ptr) } diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index 5ac4961b7cd2..7522a8604e67 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -717,7 +717,7 @@ impl InPlaceWrite for UniqueArc> { 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)? }; + unsafe { pin_init::raw_try_init(slot, init)? }; // SAFETY: All fields have been initialized. Ok(unsafe { self.assume_init() }) } @@ -727,7 +727,7 @@ impl InPlaceWrite for UniqueArc> { 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)? }; + unsafe { pin_init::raw_try_init(slot, init)? }; // SAFETY: All fields have been initialized. Ok(unsafe { self.assume_init() }.into()) } @@ -795,7 +795,7 @@ impl UniqueArc> { #[inline] pub fn init_with(mut self, init: impl Init) -> core::result::Result, E> { // SAFETY: The supplied pointer is valid for initialization. - match unsafe { init.__init(self.as_mut_ptr()) } { + match unsafe { pin_init::raw_try_init(self.as_mut_ptr(), init) } { // SAFETY: Initialization completed successfully. Ok(()) => Ok(unsafe { self.assume_init() }), Err(err) => Err(err), @@ -810,7 +810,7 @@ impl UniqueArc> { ) -> core::result::Result>, E> { // SAFETY: The supplied pointer is valid for initialization and we will later pin the value // to ensure it does not move. - match unsafe { init.__pinned_init(self.as_mut_ptr()) } { + match unsafe { pin_init::raw_try_init(self.as_mut_ptr(), init) } { // SAFETY: Initialization completed successfully. Ok(()) => Ok(unsafe { self.assume_init() }.into()), Err(err) => Err(err), diff --git a/rust/kernel/types.rs b/rust/kernel/types.rs index ac316fd7b538..67b3874cb3d2 100644 --- a/rust/kernel/types.rs +++ b/rust/kernel/types.rs @@ -417,13 +417,13 @@ impl Opaque { impl Wrapper for Opaque { /// Create an opaque pin-initializer from the given pin-initializer. - fn pin_init(slot: impl PinInit) -> impl PinInit { - Self::try_ffi_init(|ptr: *mut T| { + fn pin_init(init: impl PinInit) -> impl PinInit { + Self::try_ffi_init(|slot: *mut T| { // SAFETY: - // - `ptr` is a valid pointer to uninitialized memory, + // - `slot` is a valid pointer to uninitialized memory, // - `slot` is not accessed on error, // - `slot` is pinned in memory. - unsafe { PinInit::::__pinned_init(slot, ptr) } + unsafe { pin_init::raw_try_init(slot, init) } }) } } diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 06c18e207508..d2d186d9d78c 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -621,7 +621,7 @@ pub(crate) fn module(info: ModuleInfo) -> Result { // SAFETY: No data race, since `__MOD` can only be accessed by this module // and there only `__init` and `__exit` access it. These functions are only // called once and `__exit` cannot be called before or during `__init`. - match unsafe { initer.__pinned_init(__MOD.as_mut_ptr()) } { + match unsafe { ::pin_init::raw_try_init(__MOD.as_mut_ptr(), initer) } { Ok(m) => 0, Err(e) => e.to_errno(), } -- cgit From 1f7fa1374d3bb455944128fe5c30c19f8d3501c7 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 29 Jul 2026 16:38:47 +0100 Subject: rust: pin-init: remove `__pinned_init` method for `cfg(kernel)` Remove `__pinned_init` for kernel configuration, with all users gone. Still perserve it temporarily as deprecated so other users have time to move off it. Link: https://patch.msgid.link/20260729-merge-init-v2-5-26adf47109e7@garyguo.net Signed-off-by: Gary Guo --- rust/pin-init/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 97eaef6f2958..6e9eb90db52c 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -917,7 +917,8 @@ pub unsafe trait PinInit: Sized { /// /// Same as `__init`. #[inline(always)] - #[cfg_attr(not(kernel), deprecated = "use `raw_try_init` instead")] + #[cfg(not(kernel))] + #[deprecated = "use `raw_try_init` instead"] unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> { // SAFETY: Per safety requirement. unsafe { self.__init(slot) } -- cgit From 1e26aea0355ad2afa1ccbc62885c01f5bcfc58ca Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 3 Aug 2026 14:02:01 +0100 Subject: rust: pin-init: add `#[inline]` to small functions Currently `pin-init` crate is missing many inline annotations. They are all generic so still get inlined in normal builds, but are not inlined in `-C opt-level=s` build. Mark these functions as `#[inline]` so they are considered for inlining regardless. Signed-off-by: Gary Guo --- rust/pin-init/internal/src/pin_data.rs | 2 ++ rust/pin-init/src/__internal.rs | 5 +++++ rust/pin-init/src/alloc.rs | 4 ++++ rust/pin-init/src/lib.rs | 17 +++++++++++++++++ 4 files changed, 28 insertions(+) diff --git a/rust/pin-init/internal/src/pin_data.rs b/rust/pin-init/internal/src/pin_data.rs index 3c9d9c7364e2..ff194d27565e 100644 --- a/rust/pin-init/internal/src/pin_data.rs +++ b/rust/pin-init/internal/src/pin_data.rs @@ -468,6 +468,7 @@ fn generate_the_pin_data( impl #impl_generics ::core::clone::Clone for __ThePinData #ty_generics #whr { + #[inline] fn clone(&self) -> Self { *self } } @@ -499,6 +500,7 @@ fn generate_the_pin_data( { type PinData = __ThePinData #ty_generics; + #[inline] unsafe fn __pin_data() -> Self::PinData { __ThePinData { __phantom: ::pin_init::__internal::PhantomInvariant::new() } } diff --git a/rust/pin-init/src/__internal.rs b/rust/pin-init/src/__internal.rs index ae9a0e68cd75..8e9fd18b993f 100644 --- a/rust/pin-init/src/__internal.rs +++ b/rust/pin-init/src/__internal.rs @@ -105,6 +105,7 @@ pub unsafe trait HasInitData { pub struct AllData(PhantomInvariant); impl Clone for AllData { + #[inline] fn clone(&self) -> Self { *self } @@ -127,6 +128,7 @@ impl AllData { unsafe impl HasInitData for T { type InitData = AllData; + #[inline] unsafe fn __init_data() -> Self::InitData { AllData(PhantomInvariant::new()) } @@ -385,12 +387,14 @@ pub struct AlwaysFail { impl AlwaysFail { /// Creates a new initializer that always fails. + #[inline] pub fn new() -> Self { Self { _t: PhantomData } } } impl Default for AlwaysFail { + #[inline] fn default() -> Self { Self::new() } @@ -398,6 +402,7 @@ impl Default for AlwaysFail { // SAFETY: `__init` always fails, which is always okay. unsafe impl PinInit for AlwaysFail { + #[inline] unsafe fn __init(self, _slot: *mut T) -> Result<(), ()> { Err(()) } diff --git a/rust/pin-init/src/alloc.rs b/rust/pin-init/src/alloc.rs index 641f4c7ce890..471652e8663a 100644 --- a/rust/pin-init/src/alloc.rs +++ b/rust/pin-init/src/alloc.rs @@ -35,6 +35,7 @@ pub trait InPlaceInit: Sized { /// type. /// /// If `T: !Unpin` it will not be able to move afterwards. + #[inline] fn pin_init(init: impl PinInit) -> Result, AllocError> { // SAFETY: We delegate to `init` and only change the error type. let init = unsafe { @@ -52,6 +53,7 @@ pub trait InPlaceInit: Sized { E: From; /// Use the given initializer to in-place initialize a `T`. + #[inline] fn init(init: impl Init) -> Result { // SAFETY: We delegate to `init` and only change the error type. let init = unsafe { @@ -136,6 +138,7 @@ impl InPlaceInit for Arc { impl InPlaceWrite for Box> { type Initialized = Box; + #[inline] 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, @@ -145,6 +148,7 @@ impl InPlaceWrite for Box> { Ok(unsafe { self.assume_init() }) } + #[inline] 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, diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index 6e9eb90db52c..7600cdbbbf98 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -955,6 +955,7 @@ pub unsafe trait PinInit: Sized { /// Ok(()) /// }); /// ``` + #[inline] fn pin_chain(self, f: F) -> ChainPinInit where F: FnOnce(Pin<&mut T>) -> Result<(), E>, @@ -1003,6 +1004,7 @@ where I: PinInit, F: FnOnce(Pin<&mut T>) -> Result<(), E>, { + #[inline] unsafe fn __init(self, slot: *mut T) -> Result<(), E> { // SAFETY: All requirements fulfilled since this function is `__init`. let slot = unsafe { __internal::Slot::<__internal::Pinned, _>::new(slot) }; @@ -1068,6 +1070,7 @@ pub unsafe trait Init: PinInit { /// Ok(()) /// }); /// ``` + #[inline] fn chain(self, f: F) -> ChainInit where F: FnOnce(&mut T) -> Result<(), E>, @@ -1095,6 +1098,7 @@ where I: Init, F: FnOnce(&mut T) -> Result<(), E>, { + #[inline] unsafe fn __init(self, slot: *mut T) -> Result<(), E> { // SAFETY: All requirements fulfilled since this function is `__init`. let slot = unsafe { __internal::Slot::<__internal::Unpinned, _>::new(slot) }; @@ -1175,6 +1179,7 @@ pub const unsafe fn init_from_closure( /// /// - `*mut U` must be castable to `*mut T` and any value of type `T` written through such a /// pointer must result in a valid `U`. +#[inline] pub const unsafe fn cast_pin_init(init: impl PinInit) -> impl PinInit { // SAFETY: initialization delegated to a valid initializer. Cast is valid by function safety // requirements. @@ -1187,6 +1192,7 @@ pub const unsafe fn cast_pin_init(init: impl PinInit) -> impl Pin /// /// - `*mut U` must be castable to `*mut T` and any value of type `T` written through such a /// pointer must result in a valid `U`. +#[inline] pub const unsafe fn cast_init(init: impl Init) -> impl Init { // SAFETY: initialization delegated to a valid initializer. Cast is valid by function safety // requirements. @@ -1283,6 +1289,7 @@ where /// let array: Box<[usize; 1_000]> = Box::init(init_array_from_fn(|i| i)).unwrap(); /// assert_eq!(array.len(), 1_000); /// ``` +#[inline] pub fn init_array_from_fn( make_init: impl FnMut(usize) -> I, ) -> impl Init<[T; N], E> @@ -1307,6 +1314,7 @@ where /// Arc::pin_init(pin_init_array_from_fn(|i| CMutex::new(i))).unwrap(); /// assert_eq!(array.len(), 1_000); /// ``` +#[inline] pub fn pin_init_array_from_fn( make_init: impl FnMut(usize) -> I, ) -> impl PinInit<[T; N], E> @@ -1342,6 +1350,7 @@ where /// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the /// initializer itself will fail with that error. If it returned `Ok`, then it will run the /// initializer returned by the [`pin_init!`] invocation. +#[inline] pub fn pin_init_scope(make_init: F) -> impl PinInit where F: FnOnce() -> Result, @@ -1385,6 +1394,7 @@ where /// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the /// initializer itself will fail with that error. If it returned `Ok`, then it will run the /// initializer returned by the [`init!`] invocation. +#[inline] pub fn init_scope(make_init: F) -> impl Init where F: FnOnce() -> Result, @@ -1409,6 +1419,7 @@ unsafe impl Init for T {} // SAFETY: the `__init` function always returns `Ok(())` and initializes every field of // `slot`. Additionally, all pinning invariants of `T` are upheld. unsafe impl PinInit for T { + #[inline] unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> { // SAFETY: `slot` is valid for writes by the safety requirements of this function. unsafe { slot.write(self) }; @@ -1423,6 +1434,7 @@ unsafe impl Init for Result {} // - `Ok(())`, `slot` was initialized and all pinned invariants of `T` are upheld. // - `Err(err)`, slot was not written to. unsafe impl PinInit for Result { + #[inline] unsafe fn __init(self, slot: *mut T) -> Result<(), E> { // SAFETY: `slot` is valid for writes by the safety requirements of this function. unsafe { slot.write(self?) }; @@ -1449,6 +1461,7 @@ pub trait InPlaceWrite { impl InPlaceWrite for &'static mut MaybeUninit { type Initialized = &'static mut T; + #[inline] fn write_init(self, init: impl Init) -> Result { let slot = self.as_mut_ptr(); @@ -1459,6 +1472,7 @@ impl InPlaceWrite for &'static mut MaybeUninit { unsafe { Ok(self.assume_init_mut()) } } + #[inline] fn write_pin_init(self, init: impl PinInit) -> Result, E> { let slot = self.as_mut_ptr(); @@ -1764,6 +1778,7 @@ pub trait Wrapper { } impl Wrapper for UnsafeCell { + #[inline] fn pin_init(value_init: impl PinInit) -> impl PinInit { // SAFETY: `UnsafeCell` has a compatible layout to `T`. unsafe { cast_pin_init(value_init) } @@ -1771,6 +1786,7 @@ impl Wrapper for UnsafeCell { } impl Wrapper for MaybeUninit { + #[inline] fn pin_init(value_init: impl PinInit) -> impl PinInit { // SAFETY: `MaybeUninit` has a compatible layout to `T`. unsafe { cast_pin_init(value_init) } @@ -1779,6 +1795,7 @@ impl Wrapper for MaybeUninit { #[cfg(all(feature = "unsafe-pinned", CONFIG_RUSTC_HAS_UNSAFE_PINNED))] impl Wrapper for core::pin::UnsafePinned { + #[inline] fn pin_init(init: impl PinInit) -> impl PinInit { // SAFETY: `UnsafePinned` has a compatible layout to `T`. unsafe { cast_pin_init(init) } -- cgit From ec520816b5422f9dbedc099dad333e4b7ace32fd Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Thu, 9 Jul 2026 22:49:26 +0200 Subject: docs: rust: quick-start: remove Ubuntu 25.10 Ubuntu 25.10 is not supported anymore [1]. Thus remove it. Link: https://ubuntu.com/about/release-cycle [1] Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260709204926.139252-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- Documentation/rust/quick-start.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/rust/quick-start.rst b/Documentation/rust/quick-start.rst index a6ec3fa94d33..f79aa3c7138e 100644 --- a/Documentation/rust/quick-start.rst +++ b/Documentation/rust/quick-start.rst @@ -90,7 +90,7 @@ they should generally work out of the box, e.g.:: Ubuntu ****** -Ubuntu 25.10 and 26.04 LTS provide recent Rust releases and thus they should +Ubuntu 26.04 LTS provides recent Rust releases and thus it should generally work out of the box, e.g.:: apt install rustc rust-src bindgen rustfmt rust-clippy -- cgit From 168b4f9aa5792ebef2cb9312ca0d8c796e35d692 Mon Sep 17 00:00:00 2001 From: Harish C S Date: Sat, 11 Jul 2026 20:20:33 +0530 Subject: rust: sync: improve `Arc` documentation links The `Arc` documentation has a few mentions that do not follow the surrounding style: a plain `Arc` without an intra-doc link and a lower-case "arc". Use intra-doc links for rustdoc references to `Arc` and spell internal comments consistently as `Arc`, matching nearby docs. Suggested-by: Miguel Ojeda Link: https://github.com/Rust-for-Linux/linux/issues/1240 Signed-off-by: Harish C S Acked-by: Boqun Feng Link: https://patch.msgid.link/20260711145033.39649-1-harish.cs.ss24@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/sync/arc.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/kernel/sync/arc.rs b/rust/kernel/sync/arc.rs index 7522a8604e67..8ae0fe6f19ec 100644 --- a/rust/kernel/sync/arc.rs +++ b/rust/kernel/sync/arc.rs @@ -154,7 +154,7 @@ impl ArcInner { /// /// # Safety /// - /// `ptr` must have been returned by a previous call to [`Arc::into_raw`], and the `Arc` must + /// `ptr` must have been returned by a previous call to [`Arc::into_raw`], and the [`Arc`] must /// not yet have been destroyed. unsafe fn container_of(ptr: *const T) -> NonNull> { let refcount_layout = Layout::new::(); @@ -253,7 +253,7 @@ impl Arc { /// Convert the [`Arc`] into a raw pointer. /// - /// The raw pointer has ownership of the refcount that this Arc object owned. + /// The raw pointer has ownership of the refcount that this [`Arc`] object owned. pub fn into_raw(self) -> *const T { let ptr = self.ptr.as_ptr(); core::mem::forget(self); @@ -261,7 +261,7 @@ impl Arc { unsafe { core::ptr::addr_of!((*ptr).data) } } - /// Return a raw pointer to the data in this arc. + /// Return a raw pointer to the data in this [`Arc`]. pub fn as_ptr(this: &Self) -> *const T { let ptr = this.ptr.as_ptr(); @@ -305,7 +305,7 @@ impl Arc { /// Converts this [`Arc`] into a [`UniqueArc`], or destroys it if it is not unique. /// - /// When this destroys the `Arc`, it does so while properly avoiding races. This means that + /// When this destroys the [`Arc`], it does so while properly avoiding races. This means that /// this method will never call the destructor of the value. /// /// # Examples @@ -345,11 +345,11 @@ impl Arc { // If the refcount reaches a non-zero value, then we have destroyed this `Arc` and will // return without further touching the `Arc`. If the refcount reaches zero, then there are - // no other arcs, and we can create a `UniqueArc`. + // no other `Arc`s, and we can create a `UniqueArc`. if refcount.dec_and_test() { refcount.set(1); - // INVARIANT: We own the only refcount to this arc, so we may create a `UniqueArc`. We + // INVARIANT: We own the only refcount to this `Arc`, so we may create a `UniqueArc`. We // must pin the `UniqueArc` because the values was previously in an `Arc`, and they pin // their values. Some(Pin::from(UniqueArc { -- cgit From 39a309f432a9914af242a7a63eacfb390878c2ed Mon Sep 17 00:00:00 2001 From: Kosumi Chan Date: Sat, 11 Jul 2026 04:23:27 -0400 Subject: rust: impl_flags: use bit helper in example Use bit_u32() instead of open-coding shifts in the impl_flags! example. This demonstrates the checked bit helper and ensures that bit positions remain within the underlying u32 type. Suggested-by: Miguel Ojeda Link: https://github.com/Rust-for-Linux/linux/issues/1244 Assisted-by: OpenCode:openai/gpt-5.6-sol Signed-off-by: Kosumi Chan Suggested-by: Greg Kroah-Hartman Link: https://lore.kernel.org/rust-for-linux/2026071054-hazing-antirust-8e40@gregkh/ Reviewed-by: Alexandre Courbot Link: https://patch.msgid.link/20260711082327.3062227-1-chankocyo@gmail.com Signed-off-by: Miguel Ojeda --- rust/kernel/impl_flags.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rust/kernel/impl_flags.rs b/rust/kernel/impl_flags.rs index e2bd7639da12..fdf44d5eea9c 100644 --- a/rust/kernel/impl_flags.rs +++ b/rust/kernel/impl_flags.rs @@ -19,7 +19,10 @@ /// # Examples /// /// ``` -/// use kernel::impl_flags; +/// use kernel::{ +/// bits::bit_u32, +/// impl_flags, // +/// }; /// /// impl_flags!( /// /// Represents multiple permissions. @@ -30,13 +33,13 @@ /// #[derive(Debug, Clone, Copy, PartialEq, Eq)] /// pub enum Permission { /// /// Read permission. -/// Read = 1 << 0, +/// Read = bit_u32(0), /// /// /// Write permission. -/// Write = 1 << 1, +/// Write = bit_u32(1), /// /// /// Execute permission. -/// Execute = 1 << 2, +/// Execute = bit_u32(2), /// } /// ); /// -- cgit From 359af525750d3c13f2d500bf9425c945c9467ce3 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Fri, 7 Aug 2026 19:50:12 +0200 Subject: rust: bitfield: always inline test conversions When using the Rust GCC backend (i.e. `rustc_codegen_gcc`), GCC does not inline enough these `Bounded::from_expr` calls: /usr/bin/x86_64-linux-gnu-ld.bfd: rust/kernel.o: in function ` as core::convert::From>::from': fake.c:(.text.unlikely+0x7be): undefined reference to `rust_build_error' /usr/bin/x86_64-linux-gnu-ld.bfd: rust/kernel.o: in function ` as core::convert::From>::from': fake.c:(.text.unlikely+0x90d): undefined reference to `rust_build_error' Thus, similar to commit bc197e24a3ac ("rust: num: bounded: Always inline fits_within and from_expr"), mark them as `#[inline(always)]`. [ Reworded to add the error and to follow our usual style and sent on behalf of Antoni, who found this during his work to support Rust for Linux with the GCC backend, i.e. with `rustc_codegen_gcc`. - Miguel ] Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Antoni Boucher Acked-by: Alexandre Courbot Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260807175012.142083-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- rust/kernel/bitfield.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs index 35ede53f2b8e..a0d089423f21 100644 --- a/rust/kernel/bitfield.rs +++ b/rust/kernel/bitfield.rs @@ -581,6 +581,7 @@ mod tests { } impl From for Bounded { + #[inline(always)] fn from(mt: MemoryType) -> Bounded { Bounded::from_expr(mt as u64) } @@ -606,6 +607,7 @@ mod tests { } impl From for Bounded { + #[inline(always)] fn from(p: Priority) -> Bounded { Bounded::from_expr(p as u16) } -- cgit From e66cfc29e0d06fec34c06bb40d4d281f595677b1 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 1 Aug 2026 11:48:41 +0900 Subject: rust: bug: prevent dead_code warning from warn_on!'s flags constant Fix the following dead_code warning on some configurations in an atomic development branch: warning: constant `WARN_ON_FLAGS` is never used --> linux/rust/kernel/bug.rs:126:19 | 126 | const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN); | ^^^^^^^^^^^^^ | ::: linux/rust/kernel/sync/srcu.rs:106:12 | 106 | if crate::warn_on!( | ____________- 107 | | // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct` 108 | | // and `srcu_readers_active()` only checks the active reader count. 109 | | unsafe { bindings::srcu_readers_active(ptr) } 110 | | ) { | |_________- in this macro invocation | = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default = note: this warning originates in the macro `crate::warn_on` (in Nightly builds, run with -Z macro-backtrace for more info) The warn_on! macro always defines a WARN_ON_FLAGS constant and hands it to warn_flags!. On configurations where warn_flags! does not reference its flags argument (the LOONGARCH/ARM variant, which only calls WARN_ON(), and the !CONFIG_BUG no-op variant), the constant is left unused and triggers a dead_code warning. warn_flags! is the macro that accepts (and here discards) the flags argument, so make it responsible for the argument it drops. Also rename `_COND_STR` to `COND_STR` and consume `$file` for consistency. Fixes: dff64b072708 ("rust: Add warn_on macro") Signed-off-by: FUJITA Tomonori Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260801024841.786664-1-tomo@flapping.org [ Added newlines. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/bug.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs index ed943960f851..a09e6c9b314e 100644 --- a/rust/kernel/bug.rs +++ b/rust/kernel/bug.rs @@ -53,6 +53,10 @@ macro_rules! warn_flags { ($file:expr, $flags:expr) => { const FLAGS: u32 = $crate::bindings::BUGFLAG_WARNING | $flags; + if false { + _ = $file; + } + // SAFETY: // - `flags` and `size` are all compile-time constants, preventing // any invalid memory access. @@ -76,6 +80,10 @@ macro_rules! warn_flags { #[cfg(all(CONFIG_BUG, CONFIG_UML))] macro_rules! warn_flags { ($file:expr, $flags:expr) => { + if false { + _ = $file; + } + // SAFETY: It is always safe to call `warn_slowpath_fmt()` // with a valid null-terminated string. unsafe { @@ -94,6 +102,11 @@ macro_rules! warn_flags { #[cfg(all(CONFIG_BUG, any(CONFIG_LOONGARCH, CONFIG_ARM)))] macro_rules! warn_flags { ($file:expr, $flags:expr) => { + if false { + _ = $file; + _ = $flags; + } + // SAFETY: It is always safe to call `WARN_ON()`. unsafe { $crate::bindings::WARN_ON(true) } }; @@ -103,7 +116,12 @@ macro_rules! warn_flags { #[doc(hidden)] #[cfg(not(CONFIG_BUG))] macro_rules! warn_flags { - ($file:expr, $flags:expr) => {}; + ($file:expr, $flags:expr) => { + if false { + _ = $file; + _ = $flags; + } + }; } #[doc(hidden)] @@ -118,14 +136,14 @@ macro_rules! warn_on { let cond = $cond; #[cfg(CONFIG_DEBUG_BUGVERBOSE_DETAILED)] - const _COND_STR: &str = concat!("[", stringify!($cond), "] ", file!()); + const COND_STR: &str = concat!("[", stringify!($cond), "] ", file!()); #[cfg(not(CONFIG_DEBUG_BUGVERBOSE_DETAILED))] - const _COND_STR: &str = file!(); + const COND_STR: &str = file!(); if cond { const WARN_ON_FLAGS: u32 = $crate::bug::bugflag_taint($crate::bindings::TAINT_WARN); - $crate::warn_flags!(_COND_STR, WARN_ON_FLAGS); + $crate::warn_flags!(COND_STR, WARN_ON_FLAGS); } cond }}; -- cgit From 5d9668f3930609ead91f39b059ef4fe53db04942 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 8 Aug 2026 11:26:08 +0900 Subject: rust: bug: skip arch-specific asm in `testlib` builds Running `make rusttest` with `ARCH=` set to an architecture other than the host's fails, e.g. `ARCH=arm64` on an x86_64 host: error: invalid instruction mnemonic 'brk' --> rust/kernel/bug.rs:63:17 | 63 | / concat!( 64 | | "/* {size} */", 65 | | include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_warn_asm.rs")), 66 | | include!(concat!(env!("OBJTREE"), "/rust/kernel/generated_arch_reachable_asm.rs"))); | |_______________________________________________________________________________________________________^ | note: instantiated into assembly here --> :1:115 | 1 | /* 8 */.pushsection __bug_table,"aw"; .align 2; 14470: .long 14471f - .;.short 2305;.align 2; .popsection; 14471:brk 0x800 | ^^^ The reason is that `rusttest` builds the `kernel` crate as a host library: it passes the `CONFIG_*` cfgs of the configured architecture, but not `--target`, so code generation happens for the host. `warn_flags!` then selects the arch-specific inline asm arm based on `CONFIG_*`, and the host assembler rejects it. This does not happen with the current `master` because `warn_on!` has no user inside the `kernel` crate itself yet, but it will as soon as one is added. Reported-by: Miguel Ojeda Closes: https://lore.kernel.org/all/CANiq72n4=fz=JNKY0Jdm8BnLa=RmHB2B7s0bO47YTJ7hygqBZg@mail.gmail.com/ Signed-off-by: FUJITA Tomonori Cc: stable@vger.kernel.org Fixes: dff64b072708 ("rust: Add warn_on macro") Link: https://patch.msgid.link/20260808022608.1125174-1-tomo@flapping.org Signed-off-by: Miguel Ojeda --- rust/kernel/bug.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs index a09e6c9b314e..309131b1e9d0 100644 --- a/rust/kernel/bug.rs +++ b/rust/kernel/bug.rs @@ -8,6 +8,7 @@ #[macro_export] #[doc(hidden)] +#[cfg(not(testlib))] #[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))] #[cfg(CONFIG_DEBUG_BUGVERBOSE)] macro_rules! warn_flags { @@ -47,6 +48,7 @@ macro_rules! warn_flags { #[macro_export] #[doc(hidden)] +#[cfg(not(testlib))] #[cfg(all(CONFIG_BUG, not(CONFIG_UML), not(CONFIG_LOONGARCH), not(CONFIG_ARM)))] #[cfg(not(CONFIG_DEBUG_BUGVERBOSE))] macro_rules! warn_flags { @@ -77,6 +79,7 @@ macro_rules! warn_flags { #[macro_export] #[doc(hidden)] +#[cfg(not(testlib))] #[cfg(all(CONFIG_BUG, CONFIG_UML))] macro_rules! warn_flags { ($file:expr, $flags:expr) => { @@ -99,6 +102,7 @@ macro_rules! warn_flags { #[macro_export] #[doc(hidden)] +#[cfg(not(testlib))] #[cfg(all(CONFIG_BUG, any(CONFIG_LOONGARCH, CONFIG_ARM)))] macro_rules! warn_flags { ($file:expr, $flags:expr) => { @@ -114,7 +118,7 @@ macro_rules! warn_flags { #[macro_export] #[doc(hidden)] -#[cfg(not(CONFIG_BUG))] +#[cfg(any(testlib, not(CONFIG_BUG)))] macro_rules! warn_flags { ($file:expr, $flags:expr) => { if false { -- cgit From 30a449e04a32033d8e4844fdb4938a0ebccfa37b Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Fri, 7 Aug 2026 20:24:27 +0900 Subject: rust: bug: fix warn_on macro build error on UML Callers that go through `kernel::prelude` have `CStrExt` in scope, but code inside the `kernel` crate imports explicitly and may not. Using `warn_on!` from such a module fails to build on UML, which is the only configuration where `warn_flags!` needs a C string pointer rather than an inline asm bug entry: error[E0599]: no method named `as_char_ptr` found for reference `&ffi::CStr` in the current scope --> linux/rust/kernel/bug.rs:83:49 | 83 | $crate::c_str!(::core::file!()).as_char_ptr(), | ^^^^^^^^^^^ | ::: linux/rust/kernel/time.rs:427:9 | 427 | warn_on!(self.nanos < 0); | ------------------------ in this macro invocation | = help: items from traits can only be used if the trait is in scope = note: this error originates in the macro `$crate::warn_flags` which comes from the expansion of the macro `warn_on` (in Nightly builds, run with -Z mac) help: trait `CStrExt` which provides `as_char_ptr` is implemented but not in scope; perhaps you want to import it --> linux/rust/kernel/time.rs:27:1 | 27 + use crate::str::CStrExt; Call the method through its fully qualified path, which resolves without any import at the expansion site. Cc: stable@vger.kernel.org Fixes: dff64b072708 ("rust: Add warn_on macro") Signed-off-by: FUJITA Tomonori Link: https://patch.msgid.link/20260807112427.1039056-1-tomo@flapping.org Signed-off-by: Miguel Ojeda --- rust/kernel/bug.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs index 309131b1e9d0..3566f0234ca4 100644 --- a/rust/kernel/bug.rs +++ b/rust/kernel/bug.rs @@ -91,7 +91,7 @@ macro_rules! warn_flags { // with a valid null-terminated string. unsafe { $crate::bindings::warn_slowpath_fmt( - $crate::c_str!(::core::file!()).as_char_ptr(), + $crate::str::CStrExt::as_char_ptr($crate::c_str!(::core::file!())), line!() as $crate::ffi::c_int, $flags as $crate::ffi::c_uint, ::core::ptr::null(), -- cgit From b93fb6e76ec18cc05f76412cb3d8476f48dfdd58 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 5 Aug 2026 16:59:43 +0200 Subject: rust: error: add remaining error codes Add all of the remaining error codes from include/uapi/asm-generic/errno.h. Previous updates to error.rs have been piecemeal -- adding single error codes as needed. Instead, we can avoid future problems by adding all the remaining error code in one swoop. EDEADLOCK and EWOULDBLOCK are intentionally left out: they are just deprecated compatibility aliases of EDEADLK and EAGAIN, kept around for non-Linux/POSIX code, and have no use in new kernel code. Signed-off-by: Timur Tabi Reviewed-by: Fiona Behrens Acked-by: Danilo Krummrich Reviewed-by: Gary Guo Reviewed-by: Alexandre Courbot Signed-off-by: Philipp Stanner Link: https://patch.msgid.link/20260805145949.938505-3-phasta@kernel.org [ Formatted comments. Added the submitter's Signed-off-by tag. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/error.rs | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index a56ba6309594..e52793f77196 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -30,6 +30,7 @@ pub mod code { }; } + // From `include/uapi/asm-generic/errno-base.h`. declare_err!(EPERM, "Operation not permitted."); declare_err!(ENOENT, "No such file or directory."); declare_err!(ESRCH, "No such process."); @@ -64,9 +65,110 @@ 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."); + + // From `include/uapi/asm-generic/errno.h`. + declare_err!(EDEADLK, "Resource deadlock would occur."); + declare_err!(ENAMETOOLONG, "File name too long."); + declare_err!(ENOLCK, "No record locks available."); + declare_err!(ENOSYS, "Invalid system call number."); + declare_err!(ENOTEMPTY, "Directory not empty."); + declare_err!(ELOOP, "Too many symbolic links encountered."); + declare_err!(ENOMSG, "No message of desired type."); + declare_err!(EIDRM, "Identifier removed."); + declare_err!(ECHRNG, "Channel number out of range."); + declare_err!(EL2NSYNC, "Level 2 not synchronized."); + declare_err!(EL3HLT, "Level 3 halted."); + declare_err!(EL3RST, "Level 3 reset."); + declare_err!(ELNRNG, "Link number out of range."); + declare_err!(EUNATCH, "Protocol driver not attached."); + declare_err!(ENOCSI, "No CSI structure available."); + declare_err!(EL2HLT, "Level 2 halted."); + declare_err!(EBADE, "Invalid exchange."); + declare_err!(EBADR, "Invalid request descriptor."); + declare_err!(EXFULL, "Exchange full."); + declare_err!(ENOANO, "No anode."); + declare_err!(EBADRQC, "Invalid request code."); + declare_err!(EBADSLT, "Invalid slot."); + declare_err!(EBFONT, "Bad font file format."); + declare_err!(ENOSTR, "Device not a stream."); + declare_err!(ENODATA, "No data available."); + declare_err!(ETIME, "Timer expired."); + declare_err!(ENOSR, "Out of streams resources."); + declare_err!(ENONET, "Machine is not on the network."); + declare_err!(ENOPKG, "Package not installed."); + declare_err!(EREMOTE, "Object is remote."); + declare_err!(ENOLINK, "Link has been severed."); + declare_err!(EADV, "Advertise error."); + declare_err!(ESRMNT, "Srmount error."); + declare_err!(ECOMM, "Communication error on send."); + declare_err!(EPROTO, "Protocol error."); + declare_err!(EMULTIHOP, "Multihop attempted."); + declare_err!(EDOTDOT, "RFS specific error."); + declare_err!(EBADMSG, "Not a data message."); + declare_err!(EFSBADCRC, "Bad CRC detected."); declare_err!(EOVERFLOW, "Value too large for defined data type."); + declare_err!(ENOTUNIQ, "Name not unique on network."); + declare_err!(EBADFD, "File descriptor in bad state."); + declare_err!(EREMCHG, "Remote address changed."); + declare_err!(ELIBACC, "Can not access a needed shared library."); + declare_err!(ELIBBAD, "Accessing a corrupted shared library."); + declare_err!(ELIBSCN, ".lib section in a.out corrupted."); + declare_err!(ELIBMAX, "Attempting to link in too many shared libraries."); + declare_err!(ELIBEXEC, "Cannot exec a shared library directly."); + declare_err!(EILSEQ, "Illegal byte sequence."); + declare_err!(ERESTART, "Interrupted system call should be restarted."); + declare_err!(ESTRPIPE, "Streams pipe error."); + declare_err!(EUSERS, "Too many users."); + declare_err!(ENOTSOCK, "Socket operation on non-socket."); + declare_err!(EDESTADDRREQ, "Destination address required."); declare_err!(EMSGSIZE, "Message too long."); + declare_err!(EPROTOTYPE, "Protocol wrong type for socket."); + declare_err!(ENOPROTOOPT, "Protocol not available."); + declare_err!(EPROTONOSUPPORT, "Protocol not supported."); + declare_err!(ESOCKTNOSUPPORT, "Socket type not supported."); + declare_err!(EOPNOTSUPP, "Operation not supported on transport endpoint."); + declare_err!(EPFNOSUPPORT, "Protocol family not supported."); + declare_err!(EAFNOSUPPORT, "Address family not supported by protocol."); + declare_err!(EADDRINUSE, "Address already in use."); + declare_err!(EADDRNOTAVAIL, "Cannot assign requested address."); + declare_err!(ENETDOWN, "Network is down."); + declare_err!(ENETUNREACH, "Network is unreachable."); + declare_err!(ENETRESET, "Network dropped connection because of reset."); + declare_err!(ECONNABORTED, "Software caused connection abort."); + declare_err!(ECONNRESET, "Connection reset by peer."); + declare_err!(ENOBUFS, "No buffer space available."); + declare_err!(EISCONN, "Transport endpoint is already connected."); + declare_err!(ENOTCONN, "Transport endpoint is not connected."); + declare_err!(ESHUTDOWN, "Cannot send after transport endpoint shutdown."); + declare_err!(ETOOMANYREFS, "Too many references: cannot splice."); declare_err!(ETIMEDOUT, "Connection timed out."); + declare_err!(ECONNREFUSED, "Connection refused."); + declare_err!(EHOSTDOWN, "Host is down."); + declare_err!(EHOSTUNREACH, "No route to host."); + declare_err!(EALREADY, "Operation already in progress."); + declare_err!(EINPROGRESS, "Operation now in progress."); + declare_err!(ESTALE, "Stale file handle."); + declare_err!(EUCLEAN, "Structure needs cleaning."); + declare_err!(EFSCORRUPTED, "Filesystem is corrupted."); + declare_err!(ENOTNAM, "Not a XENIX named type file."); + declare_err!(ENAVAIL, "No XENIX semaphores available."); + declare_err!(EISNAM, "Is a named type file."); + declare_err!(EREMOTEIO, "Remote I/O error."); + declare_err!(EDQUOT, "Quota exceeded."); + declare_err!(ENOMEDIUM, "No medium found."); + declare_err!(EMEDIUMTYPE, "Wrong medium type."); + declare_err!(ECANCELED, "Operation Canceled."); + declare_err!(ENOKEY, "Required key not available."); + declare_err!(EKEYEXPIRED, "Key has expired."); + declare_err!(EKEYREVOKED, "Key has been revoked."); + declare_err!(EKEYREJECTED, "Key was rejected by service."); + declare_err!(EOWNERDEAD, "Owner died."); + declare_err!(ENOTRECOVERABLE, "State not recoverable."); + declare_err!(ERFKILL, "Operation not possible due to RF-kill."); + declare_err!(EHWPOISON, "Memory page has hardware error."); + declare_err!(EFTYPE, "Wrong file type for the intended operation."); + + // From `include/linux/errno.h`. 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."); -- cgit From 445ac1c8058035550c5a8330afb754420e712a9c Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 5 Aug 2026 16:59:44 +0200 Subject: rust: types: implement ForeignOwnable for ARef Implement ForeignOwnable for ARef, making it possible for C code to own an ARef. Since ARef represents shared ownership, BorrowedMut is &T rather than &mut T, matching the semantics of the underlying reference-counted type. Signed-off-by: Danilo Krummrich Reviewed-by: Alice Ryhl Tested-by: Daniel Almeida Signed-off-by: Philipp Stanner Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260805145949.938505-4-phasta@kernel.org [ Relaxed `'static` bound and added `#[inline]` as discussed. Added the submitter's Signed-off-by tag. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/sync/aref.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/rust/kernel/sync/aref.rs b/rust/kernel/sync/aref.rs index b721b2e00b98..9983ee085248 100644 --- a/rust/kernel/sync/aref.rs +++ b/rust/kernel/sync/aref.rs @@ -24,6 +24,11 @@ use core::{ ptr::NonNull, // }; +use crate::{ + prelude::*, + types::ForeignOwnable, // +}; + /// Types that are _always_ reference counted. /// /// It allows such types to define their own custom ref increment and decrement functions. @@ -188,6 +193,51 @@ where } impl Eq for ARef {} +// SAFETY: `into_foreign` returns a pointer from `NonNull::as_ptr`, so it's non-null. The +// `ARef` invariant guarantees that `ptr` points to a valid `T`, so it's aligned to `T`. +unsafe impl ForeignOwnable for ARef { + const FOREIGN_ALIGN: usize = core::mem::align_of::(); + + type Borrowed<'a> + = &'a T + where + Self: 'a; + type BorrowedMut<'a> + = &'a T + where + Self: 'a; + + #[inline] + fn into_foreign(self) -> *mut c_void { + ARef::into_raw(self).as_ptr().cast() + } + + #[inline] + unsafe fn from_foreign(ptr: *mut c_void) -> Self { + // SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous + // call to `Self::into_foreign`. + let ptr = unsafe { NonNull::new_unchecked(ptr.cast()) }; + + // SAFETY: `ptr` came from `into_foreign`, which consumed an `ARef` without decrementing + // the refcount, so we can transfer the ownership to the new `ARef`. + unsafe { ARef::from_raw(ptr) } + } + + #[inline] + unsafe fn borrow<'a>(ptr: *mut c_void) -> &'a T { + // SAFETY: The safety requirements of this method ensure that the object remains alive and + // immutable for the duration of 'a. + unsafe { &*ptr.cast() } + } + + #[inline] + unsafe fn borrow_mut<'a>(ptr: *mut c_void) -> &'a T { + // SAFETY: The safety requirements for `borrow_mut` are a superset of the safety + // requirements for `borrow`. + unsafe { ::borrow(ptr) } + } +} + impl PartialEq<&'_ U> for ARef where T: AlwaysRefCounted + PartialEq, -- cgit From d8973c47544371613313ecb9a8c3e7b244aa9bbf Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Wed, 5 Aug 2026 16:59:45 +0200 Subject: rust: sync: Add abstraction for rcu_barrier() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rcu_barrier() is a frequently used C function which is always safe to be called. Add a safe abstraction for rcu_barrier(). Tested-by: Daniel Almeida Signed-off-by: Philipp Stanner Acked-by: Gary Guo Reviewed-by: Onur Özkan Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260805145949.938505-5-phasta@kernel.org [ Formatted documentation. Sorted tags. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/sync/rcu.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/rust/kernel/sync/rcu.rs b/rust/kernel/sync/rcu.rs index a32bef6e490b..42d1b26a2143 100644 --- a/rust/kernel/sync/rcu.rs +++ b/rust/kernel/sync/rcu.rs @@ -50,3 +50,23 @@ impl Drop for Guard { pub fn read_lock() -> Guard { Guard::new() } + +/// Wait until all in-flight `call_rcu()` callbacks complete. +/// +/// Note that this primitive does not necessarily wait for an RCU grace period +/// to complete. For example, if there are no RCU callbacks queued anywhere +/// in the system, then [`rcu_barrier()`] is within its rights to return +/// immediately, without waiting for anything, much less an RCU grace period. +/// In fact, [`rcu_barrier()`] will normally not result in any RCU grace periods +/// beyond those that were already destined to be executed. +/// +/// In kernels built with `CONFIG_RCU_LAZY=y`, this function also hurries all +/// pending lazy RCU callbacks. +/// +/// Note that this is one of the RCU primitives which must not be called in +/// atomic context. +#[inline] +pub fn rcu_barrier() { + // SAFETY: `rcu_barrier()` is always safe to be called. It just might wait for a grace period. + unsafe { bindings::rcu_barrier() }; +} -- cgit From 409d09194c4a405af555464777e903e73a13ab78 Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Thu, 21 May 2026 14:57:13 +0800 Subject: rust: doctest: use vertical import style Convert `use` imports to vertical layout for better readability and maintainability. Signed-off-by: Alvin Sun Acked-by: David Gow Link: https://patch.msgid.link/20260521-miscdev-use-format-v3-7-56240ca70d0c@linux.dev Signed-off-by: Miguel Ojeda --- scripts/rustdoc_test_gen.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs index d61a77219a8c..ee76e96b41ee 100644 --- a/scripts/rustdoc_test_gen.rs +++ b/scripts/rustdoc_test_gen.rs @@ -31,8 +31,15 @@ use std::{ fs, fs::File, - io::{BufWriter, Read, Write}, - path::{Path, PathBuf}, + io::{ + BufWriter, + Read, + Write, // + }, + path::{ + Path, + PathBuf, // + }, // }; /// Find the real path to the original file based on the `file` portion of the test name. -- cgit From ec90dfcf05f02206c280bb59af660bbb3ae177d0 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Fri, 7 Aug 2026 22:05:31 +0900 Subject: rust: time: fix as_micros_ceil() rounding near i64::MAX The ceiling adjustment used saturating_add(NSEC_PER_USEC - 1) before dividing. Once the nanosecond value gets within NSEC_PER_USEC - 1 of i64::MAX the addition saturates to i64::MAX, which drops the ceiling bias and can yield a result one microsecond too small. Fixes: fae0cdc12340 ("rust: time: Introduce Delta type") Reported-by: Miguel Ojeda Closes: https://lore.kernel.org/rust-for-linux/CANiq72mtS0ABA2JnT5tpz6J9c_mnxY+vyPvghV_ukngWvN8F2w@mail.gmail.com/ Signed-off-by: FUJITA Tomonori Acked-by: Andreas Hindborg Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260807130531.1056209-1-tomo@flapping.org Signed-off-by: Miguel Ojeda --- rust/kernel/time.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index b8463823aed9..54fc46d460b6 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -441,22 +441,25 @@ impl Delta { /// to the value in the [`Delta`]. #[inline] pub fn as_micros_ceil(self) -> i64 { + // Only positive values need to be rounded up: truncating division already + // rounds towards zero, i.e. up, for negative values. + // + // The usual `(nanos + d - 1) / d` is not used because the addition overflows + // once `nanos` exceeds `i64::MAX - (d - 1)`; saturating the addition instead + // would drop the rounding bias and return a result one unit too small. let n = self.as_nanos(); - let n = if n >= 0 { - n.saturating_add(NSEC_PER_USEC - 1) - } else { - n - }; + + let (n, add) = if n > 0 { (n - 1, 1) } else { (n, 0) }; #[cfg(CONFIG_64BIT)] { - n / NSEC_PER_USEC + n / NSEC_PER_USEC + add } #[cfg(not(CONFIG_64BIT))] // SAFETY: It is always safe to call `ktime_to_us()` with any value. unsafe { - bindings::ktime_to_us(n) + bindings::ktime_to_us(n) + add } } -- cgit From 2e2203d885a395cb0dfbe96fffda15b1f9fa0995 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 8 Aug 2026 15:28:33 +0900 Subject: rust: time: make Delta generic over its time unit Delta hardcodes its value as i64 nanoseconds. A later patch adds a jiffies span, whose natural representation is isize jiffies rather than i64 nanoseconds, and a separate type per unit would duplicate the arithmetic and comparison machinery. Make Delta generic over its time unit so the jiffies span can reuse that machinery. The nanosecond Delta keeps its current representation and API via the default unit parameter, so no functional change. Reviewed-by: Gary Guo Signed-off-by: FUJITA Tomonori Reviewed-by: Andreas Hindborg Link: https://patch.msgid.link/20260808062839.1159990-2-tomo@flapping.org [ Reworded to remove stray word. Added intra-doc links. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/time.rs | 72 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 54fc46d460b6..9327d803b9c7 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -246,7 +246,7 @@ impl ops::Sub for Instant { #[inline] fn sub(self, other: Instant) -> Delta { Delta { - nanos: self.inner - other.inner, + value: self.inner - other.inner, } } } @@ -258,7 +258,7 @@ impl ops::Add for Instant { fn add(self, rhs: Delta) -> Self::Output { // INVARIANT: With arithmetic over/underflow checks enabled, this will panic if we overflow // (e.g. go above `KTIME_MAX`) - let res = self.inner + rhs.nanos; + let res = self.inner + rhs.value; // INVARIANT: With overflow checks enabled, we verify here that the value is >= 0 #[cfg(CONFIG_RUST_OVERFLOW_CHECKS)] @@ -278,7 +278,7 @@ impl ops::Sub for Instant { fn sub(self, rhs: Delta) -> Self::Output { // INVARIANT: With arithmetic over/underflow checks enabled, this will panic if we overflow // (e.g. go above `KTIME_MAX`) - let res = self.inner - rhs.nanos; + let res = self.inner - rhs.value; // INVARIANT: With overflow checks enabled, we verify here that the value is >= 0 #[cfg(CONFIG_RUST_OVERFLOW_CHECKS)] @@ -291,14 +291,38 @@ impl ops::Sub for Instant { } } +mod private { + pub trait Sealed {} + + impl Sealed for super::Nsec {} +} + +/// A trait for time units. +pub trait TimeUnit: private::Sealed { + /// The underlying representation of the time unit. + type Repr: Copy + Clone + PartialEq + PartialOrd + Eq + Ord + core::fmt::Debug; +} + +/// A time unit of nanoseconds. +/// +/// A [`Delta`] stores its value as [`i64`] nanoseconds and can represent +/// any [`i64`] value, including negative, zero, and positive numbers. +#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug)] +pub enum Nsec {} + +impl TimeUnit for Nsec { + type Repr = i64; +} + /// A span of time. /// -/// This struct represents a span of time, with its value stored as nanoseconds. -/// The value can represent any valid i64 value, including negative, zero, and -/// positive numbers. +/// The span is stored in the unit given by the type parameter `U` (see +/// [`TimeUnit`]); its value has type `U::Repr`. `U` defaults to [`Nsec`], so a +/// plain [`Delta`] is a span in nanoseconds. The value can be negative, zero, or +/// positive. #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug)] -pub struct Delta { - nanos: i64, +pub struct Delta { + value: U::Repr, } impl ops::Add for Delta { @@ -307,7 +331,7 @@ impl ops::Add for Delta { #[inline] fn add(self, rhs: Self) -> Self { Self { - nanos: self.nanos + rhs.nanos, + value: self.value + rhs.value, } } } @@ -315,7 +339,7 @@ impl ops::Add for Delta { impl ops::AddAssign for Delta { #[inline] fn add_assign(&mut self, rhs: Self) { - self.nanos += rhs.nanos; + self.value += rhs.value; } } @@ -325,7 +349,7 @@ impl ops::Sub for Delta { #[inline] fn sub(self, rhs: Self) -> Self::Output { Self { - nanos: self.nanos - rhs.nanos, + value: self.value - rhs.value, } } } @@ -333,7 +357,7 @@ impl ops::Sub for Delta { impl ops::SubAssign for Delta { #[inline] fn sub_assign(&mut self, rhs: Self) { - self.nanos -= rhs.nanos; + self.value -= rhs.value; } } @@ -343,7 +367,7 @@ impl ops::Mul for Delta { #[inline] fn mul(self, rhs: i64) -> Self::Output { Self { - nanos: self.nanos * rhs, + value: self.value * rhs, } } } @@ -351,7 +375,7 @@ impl ops::Mul for Delta { impl ops::MulAssign for Delta { #[inline] fn mul_assign(&mut self, rhs: i64) { - self.nanos *= rhs; + self.value *= rhs; } } @@ -362,25 +386,25 @@ impl ops::Div for Delta { fn div(self, rhs: Self) -> Self::Output { #[cfg(CONFIG_64BIT)] { - self.nanos / rhs.nanos + self.value / rhs.value } #[cfg(not(CONFIG_64BIT))] { // SAFETY: This function is always safe to call regardless of the input values - unsafe { bindings::div64_s64(self.nanos, rhs.nanos) } + unsafe { bindings::div64_s64(self.value, rhs.value) } } } } impl Delta { /// A span of time equal to zero. - pub const ZERO: Self = Self { nanos: 0 }; + pub const ZERO: Self = Self { value: 0 }; /// Create a new [`Delta`] from a number of nanoseconds. #[inline] pub const fn from_nanos(nanos: i64) -> Self { - Self { nanos } + Self { value: nanos } } /// Create a new [`Delta`] from a number of microseconds. @@ -391,7 +415,7 @@ impl Delta { #[inline] pub const fn from_micros(micros: i64) -> Self { Self { - nanos: micros.saturating_mul(NSEC_PER_USEC), + value: micros.saturating_mul(NSEC_PER_USEC), } } @@ -403,7 +427,7 @@ impl Delta { #[inline] pub const fn from_millis(millis: i64) -> Self { Self { - nanos: millis.saturating_mul(NSEC_PER_MSEC), + value: millis.saturating_mul(NSEC_PER_MSEC), } } @@ -415,7 +439,7 @@ impl Delta { #[inline] pub const fn from_secs(secs: i64) -> Self { Self { - nanos: secs.saturating_mul(NSEC_PER_SEC), + value: secs.saturating_mul(NSEC_PER_SEC), } } @@ -434,7 +458,7 @@ impl Delta { /// Return the number of nanoseconds in the [`Delta`]. #[inline] pub const fn as_nanos(self) -> i64 { - self.nanos + self.value } /// Return the smallest number of microseconds greater than or equal @@ -487,7 +511,7 @@ impl Delta { #[cfg(CONFIG_64BIT)] { Self { - nanos: self.as_nanos() % i64::from(dividend), + value: self.as_nanos() % i64::from(dividend), } } @@ -499,7 +523,7 @@ impl Delta { unsafe { bindings::div_s64_rem(self.as_nanos(), dividend, &mut rem) }; Self { - nanos: i64::from(rem), + value: i64::from(rem), } } } -- cgit From 2e0d41002a2766cfc6742be02ea8b078567200d2 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 8 Aug 2026 15:28:34 +0900 Subject: rust: time: add jiffies time unit for Delta Add a Jiffy time unit with isize as its representation and provide Delta::from_jiffies() and as_jiffies() as the unit-specific constructor and accessor, mirroring from_nanos()/as_nanos() on the nanosecond Delta. Represent the jiffies span as isize: Delta is a signed span (nanoseconds use i64) and, as a timeout, the value only needs to reach MAX_JIFFY_OFFSET ((LONG_MAX >> 1) - 1). isize is signed and matches the kernel's c_long, so it meets both. Reviewed-by: Gary Guo Signed-off-by: FUJITA Tomonori Reviewed-by: Andreas Hindborg Link: https://patch.msgid.link/20260808062839.1159990-3-tomo@flapping.org [ Added intra-doc links. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/time.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 9327d803b9c7..53b77abcb317 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -295,6 +295,7 @@ mod private { pub trait Sealed {} impl Sealed for super::Nsec {} + impl Sealed for super::Jiffy {} } /// A trait for time units. @@ -314,6 +315,17 @@ impl TimeUnit for Nsec { type Repr = i64; } +/// A time unit of jiffies. +/// +/// A [`Delta`] stores its value as [`isize`] jiffies and can represent +/// any [`isize`] value, including negative, zero, and positive numbers. +#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug)] +pub enum Jiffy {} + +impl TimeUnit for Jiffy { + type Repr = isize; +} + /// A span of time. /// /// The span is stored in the unit given by the type parameter `U` (see @@ -325,6 +337,20 @@ pub struct Delta { value: U::Repr, } +impl Delta { + /// Create a new [`Delta`] from a number of jiffies. + #[inline] + pub const fn from_jiffies(jiffies: isize) -> Self { + Self { value: jiffies } + } + + /// Return the number of jiffies in the [`Delta`]. + #[inline] + pub const fn as_jiffies(self) -> isize { + self.value + } +} + impl ops::Add for Delta { type Output = Self; -- cgit From d481d999967d4e7ebcecf472fc29b8f5dfcfcc29 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 8 Aug 2026 15:28:35 +0900 Subject: rust: time: add Delta::as_millis_ceil() Add a ceiling variant, mirroring the existing as_micros_ceil() since the existing as_millis() truncates towards zero. Reviewed-by: Gary Guo Signed-off-by: FUJITA Tomonori Reviewed-by: Andreas Hindborg Link: https://patch.msgid.link/20260808062839.1159990-4-tomo@flapping.org Signed-off-by: Miguel Ojeda --- rust/kernel/time.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs index 53b77abcb317..6c0a5e8090d0 100644 --- a/rust/kernel/time.rs +++ b/rust/kernel/time.rs @@ -528,6 +528,32 @@ impl Delta { } } + /// Return the smallest number of milliseconds greater than or equal + /// to the value in the [`Delta`]. + #[inline] + pub fn as_millis_ceil(self) -> i64 { + // Only positive values need to be rounded up: truncating division already + // rounds towards zero, i.e. up, for negative values. + // + // The usual `(nanos + d - 1) / d` is not used because the addition overflows + // once `nanos` exceeds `i64::MAX - (d - 1)`; saturating the addition instead + // would drop the rounding bias and return a result one unit too small. + let n = self.as_nanos(); + + let (n, add) = if n > 0 { (n - 1, 1) } else { (n, 0) }; + + #[cfg(CONFIG_64BIT)] + { + n / NSEC_PER_MSEC + add + } + + #[cfg(not(CONFIG_64BIT))] + // SAFETY: It is always safe to call `ktime_to_ms()` with any value. + unsafe { + bindings::ktime_to_ms(n) + add + } + } + /// Return `self % dividend` where `dividend` is in nanoseconds. /// /// The kernel doesn't have any emulation for `s64 % s64` on 32 bit platforms, so this is -- cgit From cdfcaa36ac93aa96df8310d7e57860f7600b2861 Mon Sep 17 00:00:00 2001 From: "Mukesh Kumar Chaurasiya (IBM)" Date: Tue, 11 Aug 2026 12:03:45 +0530 Subject: rust: uapi: replace direct asm-generic/ioctl.h include with linux/ioctl.h rust/uapi/uapi_helper.h was directly including instead of the proper . On powerpc, pulls in first, which defines _IOC_SIZEBITS, _IOC_DIRBITS, _IOC_NONE, and _IOC_WRITE with the arch-specific values, before falling through to . By bypassing that chain and including directly, the arch-specific overrides never ran first, so when other headers in the compilation later brought in the full arch-aware chain, Clang saw those four macros being defined a second time and emitted: clang diag: arch/powerpc/include/uapi/asm/ioctl.h:5:9: warning: '_IOC_SIZEBITS' macro redefined [-Wmacro-redefined] clang diag: arch/powerpc/include/uapi/asm/ioctl.h:6:9: warning: '_IOC_DIRBITS' macro redefined [-Wmacro-redefined] clang diag: arch/powerpc/include/uapi/asm/ioctl.h:8:9: warning: '_IOC_NONE' macro redefined [-Wmacro-redefined] clang diag: arch/powerpc/include/uapi/asm/ioctl.h:10:9: warning: '_IOC_WRITE' macro redefined [-Wmacro-redefined] Fix this by replacing the direct include of with , which is the correct arch-aware entry point and already maintains the intended include order. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202608050618.9dekfjtF-lkp@intel.com/ Signed-off-by: Mukesh Kumar Chaurasiya (IBM) Fixes: 4e1746656839 ("rust: uapi: Add UAPI crate") Link: https://patch.msgid.link/20260811063345.685884-1-mkchauras@gmail.com Signed-off-by: Miguel Ojeda --- rust/uapi/uapi_helper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/uapi/uapi_helper.h b/rust/uapi/uapi_helper.h index 06d7d1a2e8da..1c4aa4292dce 100644 --- a/rust/uapi/uapi_helper.h +++ b/rust/uapi/uapi_helper.h @@ -6,11 +6,11 @@ * Sorted alphabetically. */ -#include #include #include #include #include +#include #include #include #include -- cgit From 119b5984675cb43c2ecdf54195b418d3155eef94 Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Mon, 10 Aug 2026 22:55:23 +0900 Subject: rust: num: use const_assert! in Bounded Convert the const-block asserts in bounded.rs to const_assert!, matching the rest of the file. Signed-off-by: Eliot Courtney Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Suggested-by: Gary Guo Link: https://lore.kernel.org/rust-for-linux/DKIY9YGIPUUE.SZD2DUQM9NGK@garyguo.net/ Link: https://patch.msgid.link/20260810-pramin-split-v2-1-65a00b3c7309@nvidia.com Signed-off-by: Miguel Ojeda --- rust/kernel/num/bounded.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index dafe77782d79..9ad7df1a243d 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -485,7 +485,7 @@ where /// assert_eq!(v_shifted.get(), 0xff); /// ``` pub fn shr(self) -> Bounded { - const { assert!(RES + SHIFT >= N) } + const_assert!(RES + SHIFT >= N); // SAFETY: We shift the value right by `SHIFT`, reducing the number of bits needed to // represent the shifted value by as much, and just asserted that `RES >= N - SHIFT`. @@ -506,7 +506,7 @@ where /// assert_eq!(v_shifted.get(), 0xff00); /// ``` pub fn shl(self) -> Bounded { - const { assert!(RES >= N + SHIFT) } + const_assert!(RES >= N + SHIFT); // SAFETY: We shift the value left by `SHIFT`, augmenting the number of bits needed to // represent the shifted value by as much, and just asserted that `RES >= N + SHIFT`. -- cgit From 223aa25aee82e188ddf043a8703b16e5fdfc37d8 Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Mon, 10 Aug 2026 22:55:24 +0900 Subject: rust: num: reject Bounded::shr overshifts at build time Make `shr` reject shifts of at least the type's bit width at build time, instead of panicking or masking the shift amount at runtime. [ This implies we can break the type invariant, which in turn means we can trigger UB via `Deref`, e.g.: rust_kernel: panicked at rust/kernel/num/bounded.rs:528:22: unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached - Miguel ] Signed-off-by: Eliot Courtney Acked-by: Alexandre Courbot Reviewed-by: Gary Guo Reviewed-by: Danilo Krummrich Cc: stable@vger.kernel.org Fixes: c59a2d14cd24 ("rust: num: add `shr` and `shl` methods to `Bounded`") Link: https://patch.msgid.link/20260810-pramin-split-v2-2-65a00b3c7309@nvidia.com Signed-off-by: Miguel Ojeda --- rust/kernel/num/bounded.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index 9ad7df1a243d..90483d2c5374 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -485,6 +485,7 @@ where /// assert_eq!(v_shifted.get(), 0xff); /// ``` pub fn shr(self) -> Bounded { + const_assert!(SHIFT < T::BITS); const_assert!(RES + SHIFT >= N); // SAFETY: We shift the value right by `SHIFT`, reducing the number of bits needed to -- cgit From 8fe5e5f62bdb9660999449a4b5eaebcc37d7f842 Mon Sep 17 00:00:00 2001 From: Eliot Courtney Date: Mon, 10 Aug 2026 22:55:25 +0900 Subject: rust: num: add Bounded::shr_exact Add `shr_exact` in the vein of `try_shrink` which shifts a bounded right only if it loses no set bits. This is useful for getting a shifted down integer while simultaneously checking that it's aligned. Signed-off-by: Eliot Courtney Acked-by: Alexandre Courbot Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260810-pramin-split-v2-3-65a00b3c7309@nvidia.com Signed-off-by: Miguel Ojeda --- rust/kernel/num/bounded.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index 90483d2c5374..d192610a687d 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -493,6 +493,37 @@ where unsafe { Bounded::__new(self.0 >> SHIFT) } } + /// Right-shifts `self` by `SHIFT` if that loses no set bits, and returns the result as a + /// `Bounded<_, RES>`, where `RES >= N - SHIFT`. + /// + /// Returns [`None`] if any of the `SHIFT` least significant bits of `self` is set. + /// + /// # Examples + /// + /// ``` + /// use kernel::num::Bounded; + /// + /// let v = Bounded::::new::<0xff00>(); + /// let v_shifted: Option> = v.shr_exact::<8, _>(); + /// + /// assert_eq!(v_shifted.map(|v| v.get()), Some(0xff)); + /// + /// // A set bit would be shifted out. + /// let v = Bounded::::new::<0xff01>(); + /// let v_shifted: Option> = v.shr_exact::<8, _>(); + /// + /// assert!(v_shifted.is_none()); + /// ``` + #[inline] + pub fn shr_exact(self) -> Option> { + let shifted = self.shr::(); + if shifted.get() << SHIFT == self.0 { + Some(shifted) + } else { + None + } + } + /// Left-shifts `self` by `SHIFT` and returns the result as a `Bounded<_, RES>`, where `RES >= /// N + SHIFT`. /// -- cgit From 0752a2e96e131ef1dff9740e1d4eb4a960778edb Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Tue, 11 Aug 2026 14:39:43 +0800 Subject: rust: module: move module types into `module.rs` Move `Module`, `InPlaceModule`, `ModuleMetadata` and `ThisModule` from `lib.rs` into a new `rust/kernel/module.rs`. Re-export them from `lib.rs` to avoid tree-wide changes. Switch six bus driver registrations from `module.0` to the public `ThisModule::as_ptr()` accessor, since the field is no longer visible outside the new `module` submodule. No functional change. Assisted-by: opencode:glm-5.2 Suggested-by: Gary Guo Link: https://lore.kernel.org/all/DJFIQPLOVO4T.1K8T0VZM30LDA@garyguo.net/ Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Reviewed-by: Alice Ryhl Acked-by: Petr Pavlu Signed-off-by: Alvin Sun Link: https://patch.msgid.link/20260811-fix-fops-owner-v10-1-7e71776f9dbe@linux.dev Signed-off-by: Miguel Ojeda --- rust/kernel/auxiliary.rs | 2 +- rust/kernel/i2c.rs | 2 +- rust/kernel/lib.rs | 75 +++++------------------------------------------- rust/kernel/module.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++ rust/kernel/net/phy.rs | 6 +++- rust/kernel/pci.rs | 2 +- rust/kernel/platform.rs | 2 +- rust/kernel/usb.rs | 2 +- 8 files changed, 88 insertions(+), 74 deletions(-) create mode 100644 rust/kernel/module.rs diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index c42928d5a239..cc9745fbf179 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -69,7 +69,7 @@ unsafe impl driver::RegistrationOps for Adapter { // SAFETY: `adrv` is guaranteed to be a valid `DriverType`. to_result(unsafe { - bindings::__auxiliary_driver_register(adrv.get(), module.0, name.as_char_ptr()) + bindings::__auxiliary_driver_register(adrv.get(), module.as_ptr(), name.as_char_ptr()) }) } diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index 624b971ca8b0..dd9271af5eb8 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -142,7 +142,7 @@ unsafe impl driver::RegistrationOps for Adapter { } // SAFETY: `idrv` is guaranteed to be a valid `DriverType`. - to_result(unsafe { bindings::i2c_register_driver(module.0, idrv.get()) }) + to_result(unsafe { bindings::i2c_register_driver(module.as_ptr(), idrv.get()) }) } unsafe fn unregister(idrv: &Opaque) { diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 9512af7156df..2e175dcb145a 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -94,6 +94,7 @@ pub mod list; pub mod maple_tree; pub mod miscdevice; pub mod mm; +pub mod module; pub mod module_param; #[cfg(CONFIG_NET)] pub mod net; @@ -140,79 +141,17 @@ pub mod xarray; #[doc(hidden)] pub use bindings; pub use macros; +pub use module::{ + InPlaceModule, + Module, + ModuleMetadata, + ThisModule, // +}; pub use uapi; /// Prefix to appear before log messages printed from within the `kernel` crate. 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 + Send { - /// Called at module initialization time. - /// - /// Use this method to perform whatever setup or registration your module - /// should do. - /// - /// Equivalent to the `module_init` macro in the C API. - fn init(module: &'static ThisModule) -> error::Result; -} - -/// A module that is pinned and initialised in-place. -pub trait InPlaceModule: Sync + Send { - /// Creates an initialiser for the module. - /// - /// It is called when the module is loaded. - fn init(module: &'static ThisModule) -> impl pin_init::PinInit; -} - -impl InPlaceModule for T { - fn init(module: &'static ThisModule) -> impl pin_init::PinInit { - let initer = move |slot: *mut Self| { - let m = ::init(module)?; - - // 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`. - unsafe { pin_init::pin_init_from_closure(initer) } - } -} - -/// Metadata attached to a [`Module`] or [`InPlaceModule`]. -pub trait ModuleMetadata { - /// The name of the module as specified in the `module!` macro. - const NAME: &'static crate::str::CStr; -} - -/// Equivalent to `THIS_MODULE` in the C API. -/// -/// C header: [`include/linux/init.h`](srctree/include/linux/init.h) -pub struct ThisModule(*mut bindings::module); - -// SAFETY: `THIS_MODULE` may be used from all threads within a module. -unsafe impl Sync for ThisModule {} - -impl ThisModule { - /// Creates a [`ThisModule`] given the `THIS_MODULE` pointer. - /// - /// # Safety - /// - /// The pointer must be equal to the right `THIS_MODULE`. - pub const unsafe fn from_ptr(ptr: *mut bindings::module) -> ThisModule { - ThisModule(ptr) - } - - /// Access the raw pointer for this module. - /// - /// It is up to the user to use it correctly. - pub const fn as_ptr(&self) -> *mut bindings::module { - self.0 - } -} - #[cfg(not(testlib))] #[panic_handler] fn panic(info: &core::panic::PanicInfo<'_>) -> ! { diff --git a/rust/kernel/module.rs b/rust/kernel/module.rs new file mode 100644 index 000000000000..be242a82e86d --- /dev/null +++ b/rust/kernel/module.rs @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Module-related types and helpers. + +/// The entrypoint to implementing a kernel module. +/// +/// For any teardown or cleanup operations, your type may implement [`Drop`]. +pub trait Module: Sized + Sync + Send { + /// Called at module initialization time. + /// + /// Use this method to perform whatever setup or registration your module + /// should do. + /// + /// Equivalent to the `module_init` macro in the C API. + fn init(module: &'static ThisModule) -> crate::error::Result; +} + +/// A module that is pinned and initialised in-place. +pub trait InPlaceModule: Sync + Send { + /// Creates an initialiser for the module. + /// + /// It is called when the module is loaded. + fn init(module: &'static ThisModule) -> impl pin_init::PinInit; +} + +impl InPlaceModule for T { + fn init(module: &'static ThisModule) -> impl pin_init::PinInit { + let initer = move |slot: *mut Self| { + let m = ::init(module)?; + + // 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`. + unsafe { pin_init::pin_init_from_closure(initer) } + } +} + +/// Metadata attached to a [`Module`] or [`InPlaceModule`]. +pub trait ModuleMetadata { + /// The name of the module as specified in the `module!` macro. + const NAME: &'static crate::str::CStr; +} + +/// Equivalent to `THIS_MODULE` in the C API. +/// +/// C header: [`include/linux/init.h`](srctree/include/linux/init.h) +pub struct ThisModule(*mut crate::bindings::module); + +// SAFETY: `THIS_MODULE` may be used from all threads within a module. +unsafe impl Sync for ThisModule {} + +impl ThisModule { + /// Creates a [`ThisModule`] given the `THIS_MODULE` pointer. + /// + /// # Safety + /// + /// The pointer must be equal to the right `THIS_MODULE`. + pub const unsafe fn from_ptr(ptr: *mut crate::bindings::module) -> ThisModule { + ThisModule(ptr) + } + + /// Access the raw pointer for this module. + /// + /// It is up to the user to use it correctly. + pub const fn as_ptr(&self) -> *mut crate::bindings::module { + self.0 + } +} diff --git a/rust/kernel/net/phy.rs b/rust/kernel/net/phy.rs index 3ca99db5cccf..8b7036b8fe48 100644 --- a/rust/kernel/net/phy.rs +++ b/rust/kernel/net/phy.rs @@ -659,7 +659,11 @@ impl Registration { // the `drivers` slice are initialized properly. `drivers` will not be moved. // So it's just an FFI call. to_result(unsafe { - bindings::phy_drivers_register(drivers[0].0.get(), drivers.len().try_into()?, module.0) + bindings::phy_drivers_register( + drivers[0].0.get(), + drivers.len().try_into()?, + module.as_ptr(), + ) })?; // INVARIANT: The `drivers` slice is successfully registered to the kernel via `phy_drivers_register`. Ok(Registration { drivers }) diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 5071cae6543f..4def9ca1824c 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -86,7 +86,7 @@ unsafe impl driver::RegistrationOps for Adapter { // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`. to_result(unsafe { - bindings::__pci_register_driver(pdrv.get(), module.0, name.as_char_ptr()) + bindings::__pci_register_driver(pdrv.get(), module.as_ptr(), name.as_char_ptr()) }) } diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 9b362e0495d3..5c1d2b8c0c42 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -83,7 +83,7 @@ unsafe impl driver::RegistrationOps for Adapter { // SAFETY: `pdrv` is guaranteed to be a valid `DriverType`. to_result(unsafe { - bindings::__platform_driver_register(pdrv.get(), module.0, name.as_char_ptr()) + bindings::__platform_driver_register(pdrv.get(), module.as_ptr(), name.as_char_ptr()) }) } diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index 7aff0c82d0af..870423806e4f 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -63,7 +63,7 @@ unsafe impl driver::RegistrationOps for Adapter { // SAFETY: `udrv` is guaranteed to be a valid `DriverType`. to_result(unsafe { - bindings::usb_register_driver(udrv.get(), module.0, name.as_char_ptr()) + bindings::usb_register_driver(udrv.get(), module.as_ptr(), name.as_char_ptr()) }) } -- cgit From 54f846db7b35069959833d2f6ace998c2e11c908 Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Tue, 11 Aug 2026 14:39:44 +0800 Subject: rust: module: add `THIS_MODULE` const to `ModuleMetadata` trait Since `const_refs_to_static` has been stable as of the MSRV bump, a `ThisModule` pointer can now be used in const contexts. Add a `THIS_MODULE` const to the `ModuleMetadata` trait so that modules can provide their `ThisModule` pointer in const contexts such as static `file_operations`. Add a `this_module()` helper to retrieve the `THIS_MODULE` pointer of a given module type, and update `__init` to use it instead of the `THIS_MODULE` static generated by the `module!` macro. The `static THIS_MODULE` generated by the `module!` macro is retained for backwards compatibility with existing users and removed in a later patch once all references have been migrated. Assisted-by: opencode:glm-5.2 Reviewed-by: Andreas Hindborg Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Reviewed-by: Alice Ryhl Acked-by: Petr Pavlu Signed-off-by: Alvin Sun Link: https://patch.msgid.link/20260811-fix-fops-owner-v10-2-7e71776f9dbe@linux.dev Signed-off-by: Miguel Ojeda --- rust/kernel/module.rs | 9 +++++++++ rust/macros/module.rs | 18 +++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/rust/kernel/module.rs b/rust/kernel/module.rs index be242a82e86d..d71370598447 100644 --- a/rust/kernel/module.rs +++ b/rust/kernel/module.rs @@ -42,6 +42,15 @@ impl InPlaceModule for T { pub trait ModuleMetadata { /// The name of the module as specified in the `module!` macro. const NAME: &'static crate::str::CStr; + + /// The module's `THIS_MODULE` pointer. + const THIS_MODULE: ThisModule; +} + +/// Returns a reference to the `THIS_MODULE` of the given module type. +#[inline] +pub const fn this_module() -> &'static ThisModule { + &M::THIS_MODULE } /// Equivalent to `THIS_MODULE` in the C API. diff --git a/rust/macros/module.rs b/rust/macros/module.rs index d2d186d9d78c..b86f753997f1 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -519,6 +519,22 @@ pub(crate) fn module(info: ModuleInfo) -> Result { impl ::kernel::ModuleMetadata for #type_ { const NAME: &'static ::kernel::str::CStr = #name_cstr; + + #[cfg(MODULE)] + const THIS_MODULE: ::kernel::ThisModule = { + extern "C" { + static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>; + } + + // SAFETY: `__this_module` is constructed by the kernel at load time + // and lives until the module is unloaded. + unsafe { ::kernel::ThisModule::from_ptr(__this_module.get()) } + }; + + #[cfg(not(MODULE))] + const THIS_MODULE: ::kernel::ThisModule = unsafe { + ::kernel::ThisModule::from_ptr(::core::ptr::null_mut()) + }; } // Double nested modules, since then nobody can access the public items inside. @@ -616,7 +632,7 @@ pub(crate) fn module(info: ModuleInfo) -> Result { /// This function must only be called once. unsafe fn __init() -> ::kernel::ffi::c_int { let initer = ::init( - &super::super::THIS_MODULE + ::kernel::module::this_module::() ); // SAFETY: No data race, since `__MOD` can only be accessed by this module // and there only `__init` and `__exit` access it. These functions are only -- cgit From 98f256e2726262473bfc9e24f3f5cd3509424da4 Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Tue, 11 Aug 2026 14:39:45 +0800 Subject: rust: doctest: add LocalModule fallback for #[vtable] ThisModule Add a `LocalModule` struct with a null-pointer `ModuleMetadata` impl in the doctest harness, so that `crate::LocalModule` (auto-inserted by `#[vtable]`) resolves correctly when there is no `module!` macro. Assisted-by: opencode:glm-5.2 Reviewed-by: Andreas Hindborg Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Reviewed-by: Alice Ryhl Signed-off-by: Alvin Sun Link: https://patch.msgid.link/20260811-fix-fops-owner-v10-3-7e71776f9dbe@linux.dev [ Fixed `clippy::undocumented_unsafe_blocks` lint by wrapping with a block. Added interim `#[allow(dead_code)]`. - Miguel ] Signed-off-by: Miguel Ojeda --- scripts/rustdoc_test_gen.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs index ee76e96b41ee..2018e47c151e 100644 --- a/scripts/rustdoc_test_gen.rs +++ b/scripts/rustdoc_test_gen.rs @@ -239,6 +239,25 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{ const __LOG_PREFIX: &[u8] = b"rust_doctests_kernel\0"; +/// Dummy module type for doctest context. +#[allow(dead_code)] +struct LocalModule; + +use kernel::{{ + str::CStr, + ModuleMetadata, + ThisModule, // +}}; +use core::ptr::null_mut; + +impl ModuleMetadata for LocalModule {{ + const NAME: &'static CStr = c"rust_doctests_kernel"; + const THIS_MODULE: ThisModule = {{ + // SAFETY: `try_module_get`/`module_put` handle null module pointers gracefully. + unsafe {{ ThisModule::from_ptr(null_mut()) }} + }}; +}} + {rust_tests} "# ) -- cgit From 7d3e99e93382f1a49eab5afe78586a66907e4ff3 Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Tue, 11 Aug 2026 14:39:46 +0800 Subject: rust: macros: auto-insert OwnerModule in #[vtable] Auto-add `type OwnerModule: ::kernel::ModuleMetadata;` as a required associated type on the trait side if not already defined, and auto-insert `type OwnerModule = crate::LocalModule;` on the impl side if not explicitly provided, eliminating the need to manually declare and implement `OwnerModule` in every vtable trait and impl. Assisted-by: opencode:glm-5.2 Reviewed-by: Andreas Hindborg Suggested-by: Gary Guo Link: https://lore.kernel.org/all/DIMMWHUOLPSH.13JFRHDKDQJGO@garyguo.net Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Reviewed-by: Alice Ryhl Signed-off-by: Alvin Sun Link: https://patch.msgid.link/20260811-fix-fops-owner-v10-4-7e71776f9dbe@linux.dev [ Fixed `rusttest` by adding a dummy `LocalModule`. Removed interim `#[allow(dead_code)]`. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/macros/lib.rs | 18 ++++++++++++++++++ rust/macros/vtable.rs | 41 ++++++++++++++++++++++++++++++++++++----- scripts/rustdoc_test_gen.rs | 1 - 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 4a48fabbc268..408a90567f7e 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -177,12 +177,29 @@ pub fn module(input: TokenStream) -> TokenStream { /// /// This macro should not be used when all functions are required. /// +/// Additionally, this macro automatically handles the `OwnerModule` +/// associated type: on the trait side, `type OwnerModule: ModuleMetadata;` +/// is added as a required associated type if not already defined; on the +/// impl side, `type OwnerModule = LocalModule;` is automatically inserted +/// if not explicitly defined. +/// /// # Examples /// /// ``` /// use kernel::error::VTABLE_DEFAULT_ERROR; /// use kernel::prelude::*; /// +/// # struct LocalModule; +/// # impl kernel::ModuleMetadata for LocalModule { +/// # const NAME: &'static kernel::str::CStr = c"vtable_doctest"; +/// # +/// # // SAFETY: This doctest runs on the host: there is no `THIS_MODULE`. +/// # const THIS_MODULE: kernel::ThisModule = unsafe { +/// # kernel::ThisModule::from_ptr(core::ptr::null_mut()) +/// # }; +/// # } +/// # +/// # fn main() { /// // Declares a `#[vtable]` trait /// #[vtable] /// pub trait Operations: Send + Sync + Sized { @@ -208,6 +225,7 @@ pub fn module(input: TokenStream) -> TokenStream { /// /// assert_eq!(::HAS_FOO, true); /// assert_eq!(::HAS_BAR, false); +/// # } /// ``` /// /// [`kernel::error::VTABLE_DEFAULT_ERROR`]: ../kernel/error/constant.VTABLE_DEFAULT_ERROR.html diff --git a/rust/macros/vtable.rs b/rust/macros/vtable.rs index c6510b0c4ea1..be9a5ed8abe5 100644 --- a/rust/macros/vtable.rs +++ b/rust/macros/vtable.rs @@ -30,6 +30,22 @@ fn handle_trait(mut item: ItemTrait) -> Result { const USE_VTABLE_ATTR: (); }); + // Add `type OwnerModule: ModuleMetadata` as a required associated type if + // the trait does not already define it. + if !item + .items + .iter() + .any(|i| matches!(i, TraitItem::Type(t) if t.ident == "OwnerModule")) + { + gen_items.push(parse_quote! { + /// The module implementing this vtable trait. + /// + /// Automatically set to `crate::LocalModule` by the `#[vtable]` + /// impl macro. + type OwnerModule: ::kernel::ModuleMetadata; + }); + } + for item in &item.items { if let TraitItem::Fn(fn_item) = item { let name = &fn_item.sig.ident; @@ -57,12 +73,18 @@ fn handle_trait(mut item: ItemTrait) -> Result { fn handle_impl(mut item: ItemImpl) -> Result { let mut gen_items = Vec::new(); - let mut defined_consts = HashSet::new(); + let mut defined_items = HashSet::new(); - // Iterate over all user-defined constants to gather any possible explicit overrides. + // Iterate over all user-defined items to gather any possible explicit overrides. for item in &item.items { - if let ImplItem::Const(const_item) = item { - defined_consts.insert(const_item.ident.clone()); + match item { + ImplItem::Const(const_item) => { + defined_items.insert(const_item.ident.clone()); + } + ImplItem::Type(type_item) => { + defined_items.insert(type_item.ident.clone()); + } + _ => {} } } @@ -70,6 +92,15 @@ fn handle_impl(mut item: ItemImpl) -> Result { const USE_VTABLE_ATTR: () = (); }); + // Auto-insert `type OwnerModule = crate::LocalModule` if not explicitly defined. + // `crate::LocalModule` resolves to the real module type (via `module!`) or a + // dummy fallback in non-module contexts (e.g., doctests). + if !defined_items.contains(&parse_quote!(OwnerModule)) { + gen_items.push(parse_quote! { + type OwnerModule = crate::LocalModule; + }); + } + for item in &item.items { if let ImplItem::Fn(fn_item) = item { let name = &fn_item.sig.ident; @@ -78,7 +109,7 @@ fn handle_impl(mut item: ItemImpl) -> Result { name.span(), ); // Skip if it's declared already -- this allows user override. - if defined_consts.contains(&gen_const_name) { + if defined_items.contains(&gen_const_name) { continue; } let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs); diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs index 2018e47c151e..d087c0d9fcb3 100644 --- a/scripts/rustdoc_test_gen.rs +++ b/scripts/rustdoc_test_gen.rs @@ -240,7 +240,6 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{ const __LOG_PREFIX: &[u8] = b"rust_doctests_kernel\0"; /// Dummy module type for doctest context. -#[allow(dead_code)] struct LocalModule; use kernel::{{ -- cgit From 3ef975893041b9f826475298c802b5c046d0ba16 Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Tue, 11 Aug 2026 14:39:47 +0800 Subject: rust: drm: set fops.owner from driver module pointer Change `create_fops()` to accept an owner module pointer instead of hardcoding `null_mut()`, ensuring the kernel correctly tracks the module owning the DRM device's file operations. Assisted-by: opencode:glm-5.2 Reviewed-by: Andreas Hindborg Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Reviewed-by: Alice Ryhl Signed-off-by: Alvin Sun Link: https://patch.msgid.link/20260811-fix-fops-owner-v10-5-7e71776f9dbe@linux.dev Signed-off-by: Miguel Ojeda --- rust/kernel/drm/device.rs | 3 ++- rust/kernel/drm/gem/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 48d8b26282d1..81f9f7e59817 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -203,7 +203,8 @@ impl UnregisteredDevice { fops: &Self::GEM_FOPS, }; - const GEM_FOPS: bindings::file_operations = drm::gem::create_fops(); + const GEM_FOPS: bindings::file_operations = + drm::gem::create_fops(crate::module::this_module::().as_ptr()); /// Create a new `UnregisteredDevice` for a `drm::Driver`. /// diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index c8b66d816871..a7ba1453d40b 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -387,10 +387,10 @@ impl AllocImpl for Object { }; } -pub(super) const fn create_fops() -> bindings::file_operations { +pub(super) const fn create_fops(owner: *mut bindings::module) -> bindings::file_operations { let mut fops: bindings::file_operations = pin_init::zeroed(); - fops.owner = core::ptr::null_mut(); + fops.owner = owner; fops.open = Some(bindings::drm_open); fops.release = Some(bindings::drm_release); fops.unlocked_ioctl = Some(bindings::drm_ioctl); -- cgit From f9a2e4f3d762db99ac76609753dface6b0ee84ce Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Tue, 11 Aug 2026 14:39:48 +0800 Subject: rust: miscdevice: set fops.owner from driver module pointer Set the miscdevice fops owner field from the driver module pointer via the `this_module::()` helper, instead of defaulting to null. Assisted-by: opencode:glm-5.2 Reviewed-by: Andreas Hindborg Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Acked-by: Greg Kroah-Hartman Reviewed-by: Alice Ryhl Signed-off-by: Alvin Sun Link: https://patch.msgid.link/20260811-fix-fops-owner-v10-6-7e71776f9dbe@linux.dev Signed-off-by: Miguel Ojeda --- rust/kernel/miscdevice.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 83ce50def5ac..2a4329f98614 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -24,12 +24,13 @@ use crate::{ IovIterSource, // }, mm::virt::VmaNew, + module::this_module, prelude::*, seq_file::SeqFile, types::{ ForeignOwnable, Opaque, // - }, + }, // }; use core::marker::PhantomData; @@ -430,6 +431,7 @@ impl MiscdeviceVTable { } else { None }, + owner: this_module::().as_ptr(), ..pin_init::zeroed() }; -- cgit From 003c01b2f0bad8c8c515928938ce4e7135603884 Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Tue, 11 Aug 2026 14:39:49 +0800 Subject: rust: configfs: use `LocalModule` for `THIS_MODULE` Replace the `THIS_MODULE` static reference in the `configfs_attrs!` macro with `this_module::()`, and update rnull to import `LocalModule` instead of `THIS_MODULE`, consistent with the move of `THIS_MODULE` into the `ModuleMetadata` trait. Assisted-by: opencode:glm-5.2 Reviewed-by: Andreas Hindborg Acked-by: Danilo Krummrich Reviewed-by: Gary Guo Acked-by: Andreas Hindborg Reviewed-by: Alice Ryhl Signed-off-by: Alvin Sun Link: https://patch.msgid.link/20260811-fix-fops-owner-v10-7-7e71776f9dbe@linux.dev [ Rebased to avoid the imports cleanup patch. - Miguel ] Signed-off-by: Miguel Ojeda --- drivers/block/rnull/configfs.rs | 2 +- rust/kernel/configfs.rs | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs index 7c2eb5c0b722..32c10c3f4d0f 100644 --- a/drivers/block/rnull/configfs.rs +++ b/drivers/block/rnull/configfs.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -use super::{NullBlkDevice, THIS_MODULE}; +use super::NullBlkDevice; use kernel::{ block::mq::gen_disk::{GenDisk, GenDiskBuilder}, configfs::{self, AttributeOperations}, diff --git a/rust/kernel/configfs.rs b/rust/kernel/configfs.rs index 2339c6467325..cd082b83e9e7 100644 --- a/rust/kernel/configfs.rs +++ b/rust/kernel/configfs.rs @@ -875,13 +875,14 @@ impl ItemType { /// configfs::Subsystem, /// Configuration /// >::new_with_child_ctor::( -/// &THIS_MODULE, +/// ::kernel::module::this_module::(), /// &CONFIGURATION_ATTRS /// ); /// /// &CONFIGURATION_TPE /// } /// ``` +#[allow(clippy::crate_in_macro_def)] #[macro_export] macro_rules! configfs_attrs { ( @@ -1021,7 +1022,8 @@ macro_rules! configfs_attrs { static [< $data:upper _TPE >] : $crate::configfs::ItemType<$container, $data> = $crate::configfs::ItemType::<$container, $data>::new::( - &THIS_MODULE, &[<$ data:upper _ATTRS >] + $crate::module::this_module::(), + &[<$ data:upper _ATTRS >] ); )? @@ -1030,7 +1032,8 @@ macro_rules! configfs_attrs { $crate::configfs::ItemType<$container, $data> = $crate::configfs::ItemType::<$container, $data>:: new_with_child_ctor::( - &THIS_MODULE, &[<$ data:upper _ATTRS >] + $crate::module::this_module::(), + &[<$ data:upper _ATTRS >] ); )? -- cgit From d1ea160c4fbb439e91e544d44c8bc44ef5f29a7b Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Tue, 11 Aug 2026 14:39:50 +0800 Subject: rust_binder: use `LocalModule` for `THIS_MODULE` Replace the `THIS_MODULE` static reference in the binder fops with `this_module::()`, consistent with the move of `THIS_MODULE` into the `ModuleMetadata` trait. Assisted-by: opencode:glm-5.2 Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Reviewed-by: Alice Ryhl Signed-off-by: Alvin Sun Link: https://patch.msgid.link/20260811-fix-fops-owner-v10-8-7e71776f9dbe@linux.dev Signed-off-by: Miguel Ojeda --- drivers/android/binder/rust_binder_main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index dc1941cd2407..d6ceebbd5f94 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -17,6 +17,7 @@ use kernel::{ bindings::{self, seq_file}, fs::File, list::{ListArc, ListArcSafe, ListLinksSelfPtr, TryNewListArc}, + module::this_module, prelude::*, seq_file::SeqFile, seq_print, @@ -318,7 +319,7 @@ pub static rust_binder_fops: AssertSync = { let zeroed_ops = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; let ops = kernel::bindings::file_operations { - owner: THIS_MODULE.as_ptr(), + owner: this_module::().as_ptr(), poll: Some(rust_binder_poll), unlocked_ioctl: Some(rust_binder_ioctl), compat_ioctl: bindings::compat_ptr_ioctl, -- cgit From cdb7c5e018868e296d1586402b8e542f92b55710 Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Tue, 11 Aug 2026 14:39:51 +0800 Subject: rust: macros: remove `THIS_MODULE` static from `module!` All users have been migrated to `ModuleMetadata::THIS_MODULE` const or `this_module::()` helper. The `static THIS_MODULE` generated by the `module!` macro is no longer referenced anywhere, so remove it to avoid having two sources of the same `ThisModule` pointer. Assisted-by: opencode:glm-5.2 Reviewed-by: Andreas Hindborg Reviewed-by: Gary Guo Acked-by: Danilo Krummrich Reviewed-by: Alice Ryhl Acked-by: Petr Pavlu Signed-off-by: Alvin Sun Link: https://patch.msgid.link/20260811-fix-fops-owner-v10-9-7e71776f9dbe@linux.dev Signed-off-by: Miguel Ojeda --- rust/macros/module.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/rust/macros/module.rs b/rust/macros/module.rs index b86f753997f1..bd69d8dc4bbb 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -497,22 +497,6 @@ pub(crate) fn module(info: ModuleInfo) -> Result { /// Used by the printing macros, e.g. [`info!`]. const __LOG_PREFIX: &[u8] = #name_cstr.to_bytes_with_nul(); - // SAFETY: `__this_module` is constructed by the kernel at load time and will not be - // freed until the module is unloaded. - #[cfg(MODULE)] - static THIS_MODULE: ::kernel::ThisModule = unsafe { - extern "C" { - static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>; - }; - - ::kernel::ThisModule::from_ptr(__this_module.get()) - }; - - #[cfg(not(MODULE))] - static THIS_MODULE: ::kernel::ThisModule = unsafe { - ::kernel::ThisModule::from_ptr(::core::ptr::null_mut()) - }; - /// The `LocalModule` type is the type of the module created by `module!`, /// `module_pci_driver!`, `module_platform_driver!`, etc. type LocalModule = #type_; -- cgit From a697e26880286e68ef7efc4ba531ae97f78389c3 Mon Sep 17 00:00:00 2001 From: Alvin Sun Date: Tue, 11 Aug 2026 14:39:52 +0800 Subject: rust: module: update MAINTAINERS to cover module.rs Module types now live in `rust/kernel/module.rs` alongside `rust/kernel/module_param.rs`. Update the MODULE SUPPORT file pattern from `rust/kernel/module_param.rs` to `rust/kernel/module*.rs` so both files are covered. Assisted-by: opencode:glm-5.2 Link: https://lore.kernel.org/rust-for-linux/8ea21b29-9baf-4926-a16f-7d21c5a1a1b8@suse.com Reviewed-by: Alice Ryhl Acked-by: Petr Pavlu Signed-off-by: Alvin Sun Link: https://patch.msgid.link/20260811-fix-fops-owner-v10-10-7e71776f9dbe@linux.dev [ Removed Acked-by and Cc. - Miguel ] Signed-off-by: Miguel Ojeda --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5114e6db7307..95f6791c41bc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18192,7 +18192,7 @@ F: include/linux/module*.h F: kernel/module/ F: lib/test_kmod.c F: lib/tests/module/ -F: rust/kernel/module_param.rs +F: rust/kernel/module*.rs F: rust/macros/module.rs F: scripts/module* F: tools/testing/selftests/kmod/ -- cgit From fb7d645176189d2b068d69861e230fa5aceb4922 Mon Sep 17 00:00:00 2001 From: Ke Sun Date: Mon, 10 Aug 2026 14:35:42 +0800 Subject: rust: fmt: fix {:p} printing stack addresses The `impl_fmt_adapter_forward!` macro forwards `Pointer` for `Adapter` by destructuring `self` into a local `t`, causing `{:p}` to print the address of that temporary stack variable rather than the actual pointer. Remove `Pointer` from the macro and provide a manual impl for `Adapter<&T>` that passes `self.0` directly. Signed-off-by: Ke Sun Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Tested-by: Link Mauve Cc: stable@vger.kernel.org Fixes: c5cf01ba8dfe ("rust: support formatting of foreign types") Link: https://patch.msgid.link/20260810-hashedptr-v15-1-eafd27d36476@kylinos.cn Signed-off-by: Miguel Ojeda --- rust/kernel/fmt.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs index 73afbc51ba33..cd7d9664ff5b 100644 --- a/rust/kernel/fmt.rs +++ b/rust/kernel/fmt.rs @@ -43,7 +43,14 @@ use core::fmt::{ UpperExp, UpperHex, // }; -impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, Pointer, LowerExp, UpperExp); +impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, LowerExp, UpperExp); + +impl Pointer for Adapter<&T> { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(self.0, f) + } +} /// A copy of [`core::fmt::Display`] that allows us to implement it for foreign types. /// -- cgit From 643a7c306b8ce32743d4f94dd700c8588be37e66 Mon Sep 17 00:00:00 2001 From: Ke Sun Date: Mon, 10 Aug 2026 14:35:43 +0800 Subject: rust: fmt: route {:p} through HashedPtr to prevent address leaks Define a custom `kernel::fmt::Pointer` trait and `HashedPtr` wrapper so that `{:p}` formatting uses the kernel's `%p` hashed format instead of printing raw pointer values, preventing kernel address space leaks. Signed-off-by: Ke Sun Reviewed-by: Gary Guo Link: https://patch.msgid.link/20260810-hashedptr-v15-2-eafd27d36476@kylinos.cn [ Fixed KUnit failure when the CRNG is not ready. Then, as suggested, replaced the `scnprintf` comment (with v16's), changed width to 100, replaced cast with `without_provenance`. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/fmt.rs | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 2 deletions(-) diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs index cd7d9664ff5b..29582b053ab1 100644 --- a/rust/kernel/fmt.rs +++ b/rust/kernel/fmt.rs @@ -4,6 +4,8 @@ //! //! This module is intended to be used in place of `core::fmt` in kernel code. +use kernel::prelude::*; + pub use core::fmt::{ Arguments, Debug, @@ -39,13 +41,110 @@ use core::fmt::{ LowerExp, LowerHex, Octal, - Pointer, UpperExp, UpperHex, // }; +use core::ptr::NonNull; impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, LowerExp, UpperExp); -impl Pointer for Adapter<&T> { +/// A copy of [`core::fmt::Pointer`] that allows implementing pointer formatting for foreign types. +/// +/// Together with the [`Adapter`] type and [`fmt!`] macro, it enables raw pointer formatting to be +/// intercepted and routed to [`HashedPtr`] (kernel's `%p` hashed format), preventing kernel address +/// leaks. +/// +/// [`fmt!`]: crate::prelude::fmt! +pub trait Pointer { + /// Same as [`core::fmt::Pointer::fmt`]. + fn fmt(&self, f: &mut Formatter<'_>) -> Result; +} + +/// A wrapper for pointers that formats them using kernel's `%p` format specifier. +/// +/// By default, `%p` prints a hashed representation of the pointer address to prevent kernel address +/// leaks. When the `no_hash_pointers` kernel command-line parameter is enabled, the real address is +/// printed instead (for debugging purposes). +pub struct HashedPtr(pub *const T); + +impl Pointer for HashedPtr { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + use crate::str::CStrExt as _; + + let mut buf = [0u8; 32]; + + // Use `%#0*p` for the `0x` prefix and zero-padding; `+2` compensates for + // the prefix counting toward the field width. + let default_width = (2 * size_of::() + 2) as c_int; + let width = match (f.sign_aware_zero_pad(), f.width()) { + (true, Some(w)) if w > 0 => w.min(buf.len() - 1) as c_int, + _ => default_width, + }; + + // SAFETY: `buf` is a valid, writable 32-byte buffer, sufficient for + // all architectures (max 19 bytes for 64-bit under the default width). + // The format string is null-terminated; `width` (c_int) and pointer + // match the `%*` and `%p` specifiers. + let len = unsafe { + crate::bindings::scnprintf( + buf.as_mut_ptr().cast(), + buf.len(), + c"%#0*p".as_char_ptr(), + width, + self.0.cast::(), + ) + }; + + // SAFETY: `%#0*p` produces only ASCII, which is valid UTF-8. + let s = unsafe { core::str::from_utf8_unchecked(&buf[..len as usize]) }; + + if f.sign_aware_zero_pad() { + // `scnprintf` already applied the width and zero-padding via `%#0*p`. + f.write_str(s) + } else { + f.pad(s) + } + } +} + +// Raw pointers are formatted via `HashedPtr` (kernel `%p`: hashed by default, plain with +// `no_hash_pointers`). +impl Pointer for *const T { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(&HashedPtr(*self), f) + } +} + +impl Pointer for *mut T { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(&HashedPtr(*self), f) + } +} + +impl Pointer for &T { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(&HashedPtr(*self), f) + } +} + +impl Pointer for &mut T { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(&HashedPtr(core::ptr::from_ref(*self)), f) + } +} + +impl Pointer for NonNull { + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(&HashedPtr(self.as_ptr()), f) + } +} + +// `Adapter<&T>` bridges our `Pointer` trait to `core::fmt::Pointer` +impl core::fmt::Pointer for Adapter<&T> { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { Pointer::fmt(self.0, f) @@ -112,3 +211,88 @@ impl_display_forward!( {} crate::sync::Arc {where crate::sync::Arc: core::fmt::Display}, {} crate::sync::UniqueArc {where crate::sync::UniqueArc: core::fmt::Display}, ); + +#[macros::kunit_tests(rust_kernel_fmt)] +mod tests { + use crate::{ + bindings, + prelude::fmt, + str::CString, // + }; + + #[cfg(CONFIG_64BIT)] + mod expected { + pub(super) const PTR_VALUE: usize = 0xffffffffdeadbeef; + pub(super) const PTR_VAL_NO_CRNG: &str = "(____ptrval____)"; + pub(super) const HASHED_PREFIX: &str = "0x00000000"; + pub(super) const RAW_POINTER: &str = "0xffffffffdeadbeef"; + pub(super) const PADDED_RIGHT: &str = " 0xffffffffdeadbeef"; + pub(super) const ZERO_PADDED: &str = "0x000000ffffffffdeadbeef"; + pub(super) const HASHED_PADDED_RIGHT_PREFIX: &str = " "; + pub(super) const HASHED_ZERO_PADDED_PREFIX: &str = "0x00000000000000"; + pub(super) const CLAMPED: &str = "0x0000000000000ffffffffdeadbeef"; + } + + #[cfg(not(CONFIG_64BIT))] + mod expected { + pub(super) const PTR_VALUE: usize = 0xdeadbeef; + pub(super) const PTR_VAL_NO_CRNG: &str = "(ptrval)"; + pub(super) const HASHED_PREFIX: &str = "0x"; + pub(super) const RAW_POINTER: &str = "0xdeadbeef"; + pub(super) const PADDED_RIGHT: &str = " 0xdeadbeef"; + pub(super) const ZERO_PADDED: &str = "0x00000000000000deadbeef"; + pub(super) const HASHED_PADDED_RIGHT_PREFIX: &str = " "; + pub(super) const HASHED_ZERO_PADDED_PREFIX: &str = "0x00000000000000"; + pub(super) const CLAMPED: &str = "0x0000000000000000000000deadbeef"; + } + + #[test] + fn test_ptr_formatting() -> core::result::Result<(), crate::error::Error> { + let ptr: *const u8 = core::ptr::without_provenance(expected::PTR_VALUE); + + // SAFETY: `no_hash_pointers` is a global variable that is never concurrently modified — + // KUnit tests may run at boot (before `mark_readonly()`) or manually afterwards (when the + // variable is read-only). Reading is always safe. + let no_hash = unsafe { bindings::no_hash_pointers }; + + if no_hash { + let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?; + assert_eq!(cstr.to_str()?, expected::RAW_POINTER); + + let cstr = CString::try_from_fmt(fmt!("{:>24p}", ptr))?; + assert_eq!(cstr.to_str()?, expected::PADDED_RIGHT); + + let cstr = CString::try_from_fmt(fmt!("{:024p}", ptr))?; + assert_eq!(cstr.to_str()?, expected::ZERO_PADDED); + + let cstr = CString::try_from_fmt(fmt!("{:0100p}", ptr))?; + assert_eq!(cstr.to_str()?, expected::CLAMPED); + } else { + let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?; + let formatted = cstr.to_str()?; + // If the RNG is not yet ready, `%p` falls back to a placeholder. + if formatted == expected::PTR_VAL_NO_CRNG { + return Ok(()); + } + assert!(formatted.starts_with(expected::HASHED_PREFIX)); + assert_ne!(formatted, expected::RAW_POINTER); + + let cstr = CString::try_from_fmt(fmt!("{:>24p}", ptr))?; + assert!(cstr + .to_str()? + .starts_with(expected::HASHED_PADDED_RIGHT_PREFIX)); + + let cstr = CString::try_from_fmt(fmt!("{:024p}", ptr))?; + assert!(cstr + .to_str()? + .starts_with(expected::HASHED_ZERO_PADDED_PREFIX)); + + let cstr = CString::try_from_fmt(fmt!("{:0100p}", ptr))?; + let output = cstr.to_str()?; + assert!(output.starts_with("0x")); + assert!(!output[2..].chars().all(|c| c == '0')); + } + + Ok(()) + } +} -- cgit From a2595ed13816812cc4307a5834a584e0d06975a3 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 13 Aug 2026 09:00:49 +0000 Subject: rust: kernel: add `LocalModule` fallback for `#[vtable]` `impl`s Like commit 98f256e27262 ("rust: doctest: add LocalModule fallback for #[vtable] ThisModule"), add another `LocalModule` struct with a null-pointer `ModuleMetadata` `impl` for the `kernel` crate, so that `crate::LocalModule` (auto-inserted by `#[vtable]`) resolves correctly when there is no `module!` macro. This will be needed by DRM to use `#[vtable]` `impl` blocks in KUnit tests within the `kernel` crate [1]. Signed-off-by: Danilo Krummrich Link: https://lore.kernel.org/rust-for-linux/DKNAS52KYWLD.M15VEC6U0F6R@kernel.org/ [1] [ Created commit out of the diff in the link above. Fixed the `clippy::undocumented_unsafe_blocks` lint by wrapping with a block like in the other commit too. Added `#[allow(dead_code)]` until we actually (and unconditionally, i.e. KUnit tests may be not enabled) use it. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/lib.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 2e175dcb145a..59144e1e3d36 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -152,6 +152,20 @@ pub use uapi; /// Prefix to appear before log messages printed from within the `kernel` crate. const __LOG_PREFIX: &[u8] = b"rust_kernel\0"; +/// Dummy module type for `#[vtable]` `impl` blocks within the `kernel` crate (e.g. KUnit tests). +// The `allow` is needed since it may be unused (e.g. KUnit tests may be disabled). +#[allow(dead_code)] +struct LocalModule; + +impl ModuleMetadata for LocalModule { + const NAME: &'static str::CStr = c"rust_kernel"; + + const THIS_MODULE: ThisModule = { + // SAFETY: `try_module_get`/`module_put` handle null module pointers gracefully. + unsafe { ThisModule::from_ptr(core::ptr::null_mut()) } + }; +} + #[cfg(not(testlib))] #[panic_handler] fn panic(info: &core::panic::PanicInfo<'_>) -> ! { -- cgit From 47f27155f17498fccb1f222f79089642337498a9 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Thu, 6 Aug 2026 16:35:53 +0900 Subject: rust: add functions and traits for lossless integer conversions The core library's `From` implementations do not cover conversions that are not portable or future-proof. For instance, even though it is safe today, `From` is not implemented for `u64` because of the possibility of supporting larger-than-64bit architectures in the future. However, the kernel supports a narrower set of architectures, with a considerable amount of code that is architecture-specific. This makes it helpful and desirable to provide more infallible conversions, lest we rely on the `as` keyword and carry the risk of silently losing data. Thus, introduce a new module `num::casts` that provides safe const functions performing more conversions allowed by the build target, as well as `FromSafeCast` and `IntoSafeCast` traits that are just extensions of `From` and `Into` to conversions that are known to be lossless. Some conversions are architecture-specific: for instance, converting a `u64` to a `usize` is only lossless on 64-bit platforms. These conversions are made available via a dedicated `arch` sub-module. Suggested-by: Danilo Krummrich Link: https://lore.kernel.org/rust-for-linux/DDK4KADWJHMG.1FUPL3SDR26XF@kernel.org/ Signed-off-by: Alexandre Courbot Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/20260806-as_casts-v2-1-cb76a4d3a6ef@nvidia.com [ Added a few more intra-doc links. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/num.rs | 2 + rust/kernel/num/casts.rs | 298 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 300 insertions(+) create mode 100644 rust/kernel/num/casts.rs diff --git a/rust/kernel/num.rs b/rust/kernel/num.rs index 8532b511384c..dbe848e30efe 100644 --- a/rust/kernel/num.rs +++ b/rust/kernel/num.rs @@ -5,6 +5,8 @@ use core::ops; pub mod bounded; +pub mod casts; + pub use bounded::*; /// Designates unsigned primitive types. diff --git a/rust/kernel/num/casts.rs b/rust/kernel/num/casts.rs new file mode 100644 index 000000000000..7e6c7dec747d --- /dev/null +++ b/rust/kernel/num/casts.rs @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Helpers for performing lossless integer casts. +//! +//! The `as` keyword can be used to perform casts between integer types, but it unfortunately makes +//! no distinction between casts that are lossless, and casts from a larger type into a smaller one +//! that might silently strip data away. Thus, its use in the kernel is discouraged in favor of +//! [`From`] implementations. +//! +//! Conversely, there are casts that are lossless depending on the build architecture (such as +//! casting [`usize`] to [`u64`] on 32 or 64 bit archs), but not supported by [`From`] +//! implementations in the standard library because they are not portable. It does however make +//! sense for the kernel to support these, if only for code that is architecture-specific. +//! +//! This module provides ways to perform such conversions safely: +//! +//! - A series of const functions (e.g. [`usize_as_u64`]) supporting safe conversions in const +//! context. Conversions supported by [`From`] implementations in the standard library are also +//! covered as the [`From`] trait cannot be used in const context. +//! - Two extension traits, [`FromSafeCast`] and [`IntoSafeCast`], providing conversion methods +//! similar to [`From`] and [`Into`] for conversions that are safe to perform in the kernel, but +//! not supported by the standard library. +//! - Another series of const functions (e.g. [`u64_into_u8`]) supporting the conversion of a const +//! value from a larger type into a smaller one, provided the value fits into the destination +//! type. This is useful if a constant is defined as a larger type, but needs to be used as a +//! smaller one. +//! - An [`arch`] sub-module, defining more conversion functions that are only guaranteed to be +//! lossless for a given pointer size. These can only be used in code that is specific to a +//! given pointer size. +//! +//! # Examples +//! +//! ``` +//! use kernel::num::casts::{self, FromSafeCast, IntoSafeCast}; +//! +//! // Conversion from const context. +//! const USIZED_CONST: usize = casts::u8_as_usize(255u8); +//! +//! // Non-const conversions. +//! let a = u64::from_safe_cast(4096usize); +//! let b: u64 = 4096usize.into_safe_cast(); +//! ``` + +use crate::prelude::*; + +/// Implements safe `as` conversion functions from a given type into a series of target types. +/// +/// These functions can be used in place of `as`, with the guarantee that they will be lossless. +macro_rules! impl_safe_as { + ($from:ty as { $($into:ty),* }) => { + $( + $crate::macros::paste! { + #[doc = ::core::concat!( + "Losslessly converts a [`", + ::core::stringify!($from), + "`] into a [`", + ::core::stringify!($into), + "`].")] + /// + /// This conversion is allowed as it is always lossless. Prefer this over the `as` + /// keyword to ensure no lossy casts are performed. + /// + /// This is for use from a `const` context. For non `const` use, prefer the + /// [`FromSafeCast`] and [`IntoSafeCast`] traits. + /// + /// # Examples + /// + /// ``` + /// use kernel::num::casts; + /// + #[doc = ::core::concat!( + "assert_eq!(casts::", + ::core::stringify!($from), + "_as_", + ::core::stringify!($into), + "(1", + ::core::stringify!($from), + "), 1", + ::core::stringify!($into), + ");")] + /// ``` + #[inline] + pub const fn [<$from _as_ $into>](value: $from) -> $into { + $crate::static_assert!(size_of::<$into>() >= size_of::<$from>()); + + value as $into + } + } + )* + }; +} + +// Valid `Into` transformations. +impl_safe_as!(u8 as { u16, u32, u64, usize }); +impl_safe_as!(u16 as { u32, u64, usize }); +impl_safe_as!(u32 as { u64 }); +// A `usize` fits into a `u64` on all supported platforms. +impl_safe_as!(usize as { u64 }); +// A `u32` fits into a `usize` on all supported platforms. +impl_safe_as!(u32 as { usize }); + +/// Extension trait providing guaranteed lossless cast to [`Self`] from `T`. +/// +/// The standard library's [`From`] implementations do not cover conversions that are not portable +/// or future-proof. For instance, even though it is safe today, [`From`] is not implemented +/// for [`u64`] because of the possibility of needing to support larger-than-64bit architectures in +/// the future. +/// +/// The workaround is to either deal with the error handling of [`TryFrom`] for an operation that +/// technically cannot fail, or to use the `as` keyword, which can silently strip data if the +/// destination type is smaller than the source. +/// +/// Both options are hardly acceptable for the kernel. It is also a much more architecture +/// dependent environment, supporting only 32 and 64 bit architectures, with some modules +/// explicitly depending on a specific bus width that could greatly benefit from infallible +/// conversion operations. +/// +/// Thus this extension trait that provides, for all architectures supported by the kernel, +/// conversion methods between types for which such a cast is lossless. +/// +/// In other words, this trait is implemented if, for all supported targets and with `t: T`, the +/// `t as Self` operation is completely lossless. +/// +/// Prefer this over the `as` keyword to guarantee that no lossy casts are performed. +/// +/// If you need to perform a conversion in `const` context, use [`u32_as_usize`], [`usize_as_u64`], +/// etc. +/// +/// # Examples +/// +/// ``` +/// use kernel::num::casts::FromSafeCast; +/// +/// assert_eq!(usize::from_safe_cast(0xf00u32), 0xf00usize); +/// ``` +pub trait FromSafeCast { + /// Create a [`Self`] from `value`. This operation is guaranteed to be lossless. + fn from_safe_cast(value: T) -> Self; +} + +// A `usize` fits into a `u64` on all supported platforms. +impl FromSafeCast for u64 { + #[inline] + fn from_safe_cast(value: usize) -> Self { + usize_as_u64(value) + } +} + +// A `u32` fits into a `usize` on all supported platforms. +impl FromSafeCast for usize { + #[inline] + fn from_safe_cast(value: u32) -> Self { + u32_as_usize(value) + } +} + +/// Counterpart to the [`FromSafeCast`] trait, i.e. this trait is to [`FromSafeCast`] what [`Into`] +/// is to [`From`]. +/// +/// See the documentation of [`FromSafeCast`] for the motivation. +/// +/// # Examples +/// +/// ``` +/// use kernel::num::casts::IntoSafeCast; +/// +/// assert_eq!(0xf00usize, 0xf00u32.into_safe_cast()); +/// ``` +pub trait IntoSafeCast { + /// Convert `self` into a `T`. This operation is guaranteed to be lossless. + fn into_safe_cast(self) -> T; +} + +/// Reverse operation for types implementing [`FromSafeCast`]. +impl IntoSafeCast for S +where + T: FromSafeCast, +{ + #[inline] + fn into_safe_cast(self) -> T { + T::from_safe_cast(self) + } +} + +/// Implements lossless conversion of a constant from a larger type into a smaller one. +macro_rules! impl_const_into { + ($from:ty => { $($into:ty),* }) => { + $( + $crate::macros::paste! { + #[doc = ::core::concat!( + "Performs a build-time safe conversion of a [`", + ::core::stringify!($from), + "`] constant value into a [`", + ::core::stringify!($into), + "`].")] + /// + /// This checks at compile-time that the conversion is lossless, and triggers a build + /// error if it isn't. + /// + /// # Examples + /// + /// ``` + /// use kernel::num::casts; + /// + /// // Succeeds because the value of the source fits into the destination's type. + #[doc = ::core::concat!( + "assert_eq!(casts::", + ::core::stringify!($from), + "_into_", + ::core::stringify!($into), + "::<1", + ::core::stringify!($from), + ">(), 1", + ::core::stringify!($into), + ");")] + /// ``` + #[inline] + pub const fn [<$from _into_ $into>]() -> $into { + // Make sure that the target type is smaller than the source one. + $crate::static_assert!($from::BITS >= $into::BITS); + // CAST: we statically enforced above that `$from` is larger than `$into`, so the + // `as` conversion will be lossless. + $crate::const_assert!(N >= $into::MIN as $from && N <= $into::MAX as $from); + + N as $into + } + } + )* + }; +} + +impl_const_into!(usize => { u8, u16, u32 }); +impl_const_into!(u64 => { u8, u16, u32 }); +impl_const_into!(u32 => { u8, u16 }); +impl_const_into!(u16 => { u8 }); + +/// Conversions that are only lossless for the current architecture. +/// +/// # Portability +/// +/// Callers of this module become dependent on the setting of `CONFIG_64BIT`. Use with caution, and +/// never in code that is portable across pointer sizes. +pub mod arch { + /// Trait identical to [`FromSafeCast`](super::FromSafeCast), but for conversions that are not + /// available on all architectures. + pub trait FromSafeCastArch { + /// Create a [`Self`] from `value`. This operation is guaranteed to be lossless. + fn from_safe_cast_arch(value: T) -> Self; + } + + /// Trait identical to [`IntoSafeCast`](super::IntoSafeCast), but for conversions that are not + /// available on all architectures. + pub trait IntoSafeCastArch { + /// Convert `self` into a `T`. This operation is guaranteed to be lossless. + fn into_safe_cast_arch(self) -> T; + } + + /// Reverse operation for types implementing [`FromSafeCastArch`]. + impl IntoSafeCastArch for S + where + T: FromSafeCastArch, + { + #[inline] + fn into_safe_cast_arch(self) -> T { + T::from_safe_cast_arch(self) + } + } + + /// A [`u64`] fits into a [`usize`] on 64-bit platforms. + #[cfg(CONFIG_64BIT)] + #[inline] + pub const fn u64_as_usize(value: u64) -> usize { + value as usize + } + + #[cfg(CONFIG_64BIT)] + impl FromSafeCastArch for usize { + #[inline] + fn from_safe_cast_arch(value: u64) -> Self { + u64_as_usize(value) + } + } + + /// A [`usize`] fits into a [`u32`] on 32-bit platforms. + #[cfg(not(CONFIG_64BIT))] + #[inline] + pub const fn usize_as_u32(value: usize) -> u32 { + value as u32 + } + + #[cfg(not(CONFIG_64BIT))] + impl FromSafeCastArch for u32 { + #[inline] + fn from_safe_cast_arch(value: usize) -> Self { + usize_as_u32(value) + } + } +} -- cgit