summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-09 21:05:25 +0200
committerMark Brown <broonie@kernel.org>2026-10-09 21:05:25 +0200
commitbfe67cc3798287bfca334d92f342e093f233f2c2 (patch)
tree9cbf2394ec9f808731babf5a73cf965a421660c0
parenteb413c99c921f34ccb49044c884d540c025990d8 (diff)
parent2dee3d3adcadcc57d62ba6608cd02f50b0c10264 (diff)
downloadlinux-next-bfe67cc3798287bfca334d92f342e093f233f2c2.tar.gz
linux-next-bfe67cc3798287bfca334d92f342e093f233f2c2.zip
Merge branch 'rust-next' of https://github.com/Rust-for-Linux/linux.git
-rw-r--r--Documentation/rust/general-information.rst4
-rw-r--r--init/Kconfig3
-rw-r--r--rust/.gitignore1
-rw-r--r--rust/Makefile11
-rw-r--r--rust/bindgen_parameters2
-rw-r--r--rust/bindings/bindings_helper.h2
-rw-r--r--rust/kernel/Kconfig.test11
-rw-r--r--rust/kernel/bitfield.rs1
-rw-r--r--rust/kernel/bug.rs33
-rw-r--r--rust/kernel/kunit.rs12
-rw-r--r--rust/kernel/lib.rs1
-rw-r--r--rust/kernel/mem.rs70
-rw-r--r--rust/kernel/miscdevice.rs35
-rw-r--r--rust/kernel/str.rs2
-rw-r--r--rust/macros/helpers.rs16
-rw-r--r--rust/macros/kunit.rs5
-rw-r--r--rust/macros/lib.rs48
-rw-r--r--rust/macros/module.rs2
-rw-r--r--rust/macros/vtable.rs43
-rwxr-xr-xscripts/remove-stale-files2
-rw-r--r--scripts/rustdoc_test_builder.rs7
-rw-r--r--scripts/rustdoc_test_gen.rs73
22 files changed, 226 insertions, 158 deletions
diff --git a/Documentation/rust/general-information.rst b/Documentation/rust/general-information.rst
index 09234bed272c..20e3178ebfd4 100644
--- a/Documentation/rust/general-information.rst
+++ b/Documentation/rust/general-information.rst
@@ -157,5 +157,5 @@ numerical comparisons, one may define a new Kconfig symbol:
.. code-block:: kconfig
- config RUSTC_HAS_SPAN_FILE
- def_bool RUSTC_VERSION >= 108800
+ config RUSTC_HAS_FILE_AS_C_STR
+ def_bool RUSTC_VERSION >= 109100
diff --git a/init/Kconfig b/init/Kconfig
index ad592fdf29af..dfcf108c21d9 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -183,9 +183,6 @@ config LD_CAN_USE_KEEP_IN_OVERLAY
# https://github.com/llvm/llvm-project/pull/130661
def_bool LD_IS_BFD || LLD_VERSION >= 210000
-config RUSTC_HAS_SPAN_FILE
- def_bool RUSTC_VERSION >= 108800
-
config RUSTC_HAS_UNNECESSARY_TRANSMUTES
def_bool RUSTC_VERSION >= 108800
diff --git a/rust/.gitignore b/rust/.gitignore
index d3829ffab80b..5ba6b490d1e9 100644
--- a/rust/.gitignore
+++ b/rust/.gitignore
@@ -3,7 +3,6 @@
bindings_generated.rs
bindings_helpers_generated.rs
doctests_kernel_generated.rs
-doctests_kernel_generated_kunit.c
uapi_generated.rs
exports_*_generated.h
doc/
diff --git a/rust/Makefile b/rust/Makefile
index da1a7409d984..320ab6bb36c4 100644
--- a/rust/Makefile
+++ b/rust/Makefile
@@ -39,10 +39,8 @@ obj-$(CONFIG_RUST) += exports.o
always-$(CONFIG_RUST) += host/libproc_macro2.rlib host/libquote.rlib host/libsyn.rlib
always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.rs
-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.c
obj-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.o
-obj-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.o
always-$(subst y,$(CONFIG_RUST),$(CONFIG_JUMP_LABEL)) += kernel/generated_arch_static_branch_asm.rs
ifndef CONFIG_UML
@@ -53,7 +51,6 @@ endif
ifdef CONFIG_RUST
procmacro-name = $(shell MAKEFLAGS= $(RUSTC) --print file-names --crate-name $(1) --crate-type proc-macro - </dev/null)
-procmacro-extension := $(patsubst libname.%,%,$(call procmacro-name,name))
libzerocopy_derive_name := $(call procmacro-name,zerocopy_derive)
libmacros_name := $(call procmacro-name,macros)
@@ -75,7 +72,8 @@ endif
cfgs-to-flags = $(patsubst %,--cfg='%',$1)
core-cfgs := \
- no_fp_fmt_parse
+ no_fp_fmt_parse \
+ no_io_statics
core-edition := $(if $(call rustc-min-version,108700),2024,2021)
@@ -85,6 +83,7 @@ core-skip_flags := \
core-flags := \
--edition=$(core-edition) \
+ -Aunused_variables \
$(call cfgs-to-flags,$(core-cfgs))
zerocopy-cfgs := \
@@ -400,7 +399,7 @@ quiet_cmd_rustdoc_test_kernel = RUSTDOC TK $<
$< $(rustdoc_test_kernel_quiet); \
$(objtree)/scripts/rustdoc_test_gen
-%/doctests_kernel_generated.rs %/doctests_kernel_generated_kunit.c: \
+%/doctests_kernel_generated.rs: \
$(src)/kernel/lib.rs $(obj)/kernel.o \
$(objtree)/scripts/rustdoc_test_builder \
$(objtree)/scripts/rustdoc_test_gen FORCE
@@ -620,7 +619,7 @@ quiet_cmd_rustc_procmacro = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET))
-Clink-args='$(call escsq,$(KBUILD_PROCMACROLDFLAGS))' \
--emit=dep-info=$(depfile) --emit=link=$@ --extern proc_macro \
--crate-type proc-macro -L$(objtree)/$(obj)/host \
- --crate-name $(patsubst lib%.$(procmacro-extension),%,$(notdir $@)) \
+ --crate-name $(basename $(@F:lib%=%)) \
@$(objtree)/include/generated/rustc_cfg $<
# Procedural macros can only be used with the `rustc` that compiled it.
diff --git a/rust/bindgen_parameters b/rust/bindgen_parameters
index 8402b0c93545..93e622aaaf78 100644
--- a/rust/bindgen_parameters
+++ b/rust/bindgen_parameters
@@ -50,7 +50,7 @@
--blocklist-item VM_MAYREAD
--blocklist-item VM_MAYWRITE
--blocklist-item VM_MAYEXEC
---blocklist-item VM_MAYEXEC
+--blocklist-item VM_MAYSHARE
--blocklist-item VM_PFNMAP
--blocklist-item VM_IO
--blocklist-item VM_DONTCOPY
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 4b31aa7f432f..c706dda5b5ca 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -138,7 +138,7 @@ const vm_flags_t RUST_CONST_HELPER_VM_SHARED = VM_SHARED;
const vm_flags_t RUST_CONST_HELPER_VM_MAYREAD = VM_MAYREAD;
const vm_flags_t RUST_CONST_HELPER_VM_MAYWRITE = VM_MAYWRITE;
const vm_flags_t RUST_CONST_HELPER_VM_MAYEXEC = VM_MAYEXEC;
-const vm_flags_t RUST_CONST_HELPER_VM_MAYSHARE = VM_MAYEXEC;
+const vm_flags_t RUST_CONST_HELPER_VM_MAYSHARE = VM_MAYSHARE;
const vm_flags_t RUST_CONST_HELPER_VM_PFNMAP = VM_PFNMAP;
const vm_flags_t RUST_CONST_HELPER_VM_IO = VM_IO;
const vm_flags_t RUST_CONST_HELPER_VM_DONTCOPY = VM_DONTCOPY;
diff --git a/rust/kernel/Kconfig.test b/rust/kernel/Kconfig.test
index e6a5c7a795f0..966b07872ba1 100644
--- a/rust/kernel/Kconfig.test
+++ b/rust/kernel/Kconfig.test
@@ -83,4 +83,15 @@ config RUST_BITFIELD_KUNIT_TEST
If unsure, say N.
+config RUST_BUG_KUNIT_TEST
+ bool "KUnit tests for the Rust BUG/WARN functionality" if !KUNIT_ALL_TESTS
+ depends on BUG
+ default KUNIT_ALL_TESTS
+ help
+ This option enables KUnit tests for the Rust BUG/WARN functionality.
+ These are only for development and testing, not for regular
+ kernel use cases.
+
+ If unsure, say N.
+
endif
diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
index a0d089423f21..861522c8cf3e 100644
--- a/rust/kernel/bitfield.rs
+++ b/rust/kernel/bitfield.rs
@@ -295,7 +295,6 @@ macro_rules! bitfield {
$(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* }
) => {
$crate::bitfield!(@core
- #[allow(non_camel_case_types)]
$(#[$attr])* $vis $name $storage
);
$crate::bitfield!(@fields $vis $name $storage { $($fields)* });
diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs
index 3566f0234ca4..6abed2c055b1 100644
--- a/rust/kernel/bug.rs
+++ b/rust/kernel/bug.rs
@@ -85,6 +85,7 @@ macro_rules! warn_flags {
($file:expr, $flags:expr) => {
if false {
_ = $file;
+ _ = $flags;
}
// SAFETY: It is always safe to call `warn_slowpath_fmt()`
@@ -93,7 +94,7 @@ macro_rules! warn_flags {
$crate::bindings::warn_slowpath_fmt(
$crate::str::CStrExt::as_char_ptr($crate::c_str!(::core::file!())),
line!() as $crate::ffi::c_int,
- $flags as $crate::ffi::c_uint,
+ $crate::bindings::TAINT_WARN,
::core::ptr::null(),
);
}
@@ -152,3 +153,33 @@ macro_rules! warn_on {
cond
}};
}
+
+#[cfg(CONFIG_RUST_BUG_KUNIT_TEST)]
+#[macros::kunit_tests(rust_kernel_bug)]
+mod tests {
+ // The counter is incremented by the kernel warning path, not by `warn_on!`
+ // itself. A count of one means the warning was really reported.
+ #[test]
+ fn test_warn_on() {
+ // SAFETY: `kunit_get_current_test()` is always safe to call (it has
+ // fallbacks for when no KUnit test is running).
+ let test = unsafe { bindings::kunit_get_current_test() };
+
+ // SAFETY: This function runs only as a KUnit test case, so `test` is a
+ // valid pointer to the running test.
+ let handle = unsafe { bindings::kunit_start_suppress_warning(test) };
+
+ assert!(!warn_on!(false));
+ assert!(warn_on!(true));
+
+ // SAFETY: `kunit_suppressed_warning_count()` accepts any value returned by
+ // `kunit_start_suppress_warning()`.
+ let suppressed_count = unsafe { bindings::kunit_suppressed_warning_count(handle) };
+
+ // SAFETY: `test` is valid as above. `kunit_end_suppress_warning()` accepts any
+ // value returned by `kunit_start_suppress_warning()`.
+ unsafe { bindings::kunit_end_suppress_warning(test, handle) };
+
+ assert_eq!(suppressed_count, 1);
+ }
+}
diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs
index 91eaff8c186a..25ff272d29b2 100644
--- a/rust/kernel/kunit.rs
+++ b/rust/kernel/kunit.rs
@@ -58,15 +58,17 @@ pub fn info(args: fmt::Arguments<'_>) {
#[doc(hidden)]
#[macro_export]
macro_rules! kunit_assert {
- ($name:literal, $file:literal, $diff:expr, $condition:expr $(,)?) => {
+ ($name:literal, $condition:expr $(,)?) => {
'out: {
// Do nothing if the condition is `true`.
if $condition {
break 'out;
}
- static FILE: &'static $crate::str::CStr = $file;
- static LINE: i32 = ::core::line!() as i32 - $diff;
+ // Use `file!()` instead of `::core::file!()` here so it can be overridden.
+ static FILE: &'static $crate::str::CStr = $crate::c_str!(file!());
+ // Use `line!()` instead of `::core::line!()` here so it can be overridden.
+ static LINE: i32 = line!() as i32;
static CONDITION: &'static $crate::str::CStr = $crate::c_str!(stringify!($condition));
// SAFETY: FFI call without safety requirements.
@@ -164,10 +166,10 @@ macro_rules! kunit_assert {
#[doc(hidden)]
#[macro_export]
macro_rules! kunit_assert_eq {
- ($name:literal, $file:literal, $diff:expr, $left:expr, $right:expr $(,)?) => {{
+ ($name:literal, $left:expr, $right:expr $(,)?) => {{
// For the moment, we just forward to the expression assert because, for binary asserts,
// KUnit supports only a few types (e.g. integers).
- $crate::kunit_assert!($name, $file, $diff, $left == $right);
+ $crate::kunit_assert!($name, $left == $right);
}};
}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 4d5c96ddc49c..7225abc64084 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -98,6 +98,7 @@ pub mod jump_label;
pub mod kunit;
pub mod list;
pub mod maple_tree;
+pub mod mem;
pub mod miscdevice;
pub mod mm;
pub mod module;
diff --git a/rust/kernel/mem.rs b/rust/kernel/mem.rs
new file mode 100644
index 000000000000..33676760bee2
--- /dev/null
+++ b/rust/kernel/mem.rs
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Basic utilities for dealing with memory, values, and types.
+
+// TODO: `Alignment` should be moved here as well, Rust moved it upstream in 1.96.
+pub use crate::ptr::Alignment;
+
+/// Trait indicating that [`Align<N>`] is a valid alignment.
+pub trait ValidAlign {
+ #[doc(hidden)]
+ type Repr: Copy;
+}
+
+/// Zero-sized type that provides the alignment specified via the generic parameter.
+///
+/// # Examples
+///
+/// ```
+/// # use kernel::mem::Align;
+/// const ALIGN: usize = 128;
+/// struct MyStruct {
+/// _align: Align<ALIGN>,
+/// }
+/// assert_eq!(align_of::<MyStruct>(), ALIGN);
+/// ```
+#[repr(transparent)]
+#[derive(Default, Clone, Copy)]
+pub struct Align<const N: usize>([<Self as ValidAlign>::Repr; 0])
+where
+ Self: ValidAlign;
+
+impl<const N: usize> Align<N>
+where
+ Self: ValidAlign,
+{
+ /// Create a new [`Align<N>`].
+ #[inline]
+ pub const fn new() -> Self {
+ Align([])
+ }
+}
+
+macro_rules! impl_align {
+ () => {};
+ ($a:literal $($rest:literal)*) => {
+ const _: () = {
+ #[repr(align($a))]
+ #[derive(Clone, Copy)]
+ pub struct Repr;
+
+ impl ValidAlign for Align<$a> {
+ type Repr = Repr;
+ }
+ };
+
+ impl_align!($($rest)*);
+ }
+}
+
+impl_align!(1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536);
+
+impl<const N: usize> core::fmt::Debug for Align<N>
+where
+ Self: ValidAlign,
+{
+ #[inline]
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ write!(f, "Align<{N}>")
+ }
+}
diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs
index 8d4b719bd83f..eacea29e3fb0 100644
--- a/rust/kernel/miscdevice.rs
+++ b/rust/kernel/miscdevice.rs
@@ -11,10 +11,7 @@
use crate::{
bindings,
device::Device,
- error::{
- to_result,
- VTABLE_DEFAULT_ERROR, //
- },
+ error::to_result,
fs::{
File,
Kiocb, //
@@ -145,37 +142,33 @@ pub trait MiscDevice: Sized {
/// the VMA initialization by calling methods of `vma`. If the function does not return an
/// error, the kernel will complete initialization of the VMA according to the properties of
/// `vma`.
+ #[optional]
fn mmap(
_device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
_file: &File,
_vma: &VmaNew,
- ) -> Result {
- build_error!(VTABLE_DEFAULT_ERROR)
- }
+ ) -> Result;
/// Read from this miscdevice.
- fn read_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterDest<'_>) -> Result<usize> {
- build_error!(VTABLE_DEFAULT_ERROR)
- }
+ #[optional]
+ fn read_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterDest<'_>) -> Result<usize>;
/// Write to this miscdevice.
- fn write_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterSource<'_>) -> Result<usize> {
- build_error!(VTABLE_DEFAULT_ERROR)
- }
+ #[optional]
+ fn write_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterSource<'_>) -> Result<usize>;
/// Handler for ioctls.
///
/// The `cmd` argument is usually manipulated using the utilities in [`kernel::ioctl`].
///
/// [`kernel::ioctl`]: mod@crate::ioctl
+ #[optional]
fn ioctl(
_device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
_file: &File,
_cmd: u32,
_arg: usize,
- ) -> Result<isize> {
- build_error!(VTABLE_DEFAULT_ERROR)
- }
+ ) -> Result<isize>;
/// Handler for ioctls.
///
@@ -185,23 +178,21 @@ pub trait MiscDevice: Sized {
/// that have different layout on 32-bit and 64-bit userspace. If no implementation is
/// provided, then `compat_ptr_ioctl` will be used instead.
#[cfg(CONFIG_COMPAT)]
+ #[optional]
fn compat_ioctl(
_device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
_file: &File,
_cmd: u32,
_arg: usize,
- ) -> Result<isize> {
- build_error!(VTABLE_DEFAULT_ERROR)
- }
+ ) -> Result<isize>;
/// Show info for this fd.
+ #[optional]
fn show_fdinfo(
_device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
_m: &SeqFile,
_file: &File,
- ) {
- build_error!(VTABLE_DEFAULT_ERROR)
- }
+ );
}
/// A vtable for the file operations of a Rust miscdevice.
diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs
index b3caa9a1c898..644b4279a116 100644
--- a/rust/kernel/str.rs
+++ b/rust/kernel/str.rs
@@ -411,7 +411,7 @@ impl AsRef<BStr> for CStr {
/// const BAD: &CStr = c_str!("literal");
///
/// // `c_str!` is still needed for static non-literal C strings.
-/// const GOOD: &CStr = c_str!(concat!(file!(), ":", line!(), ": My CStr!"));
+/// const GOOD: &CStr = c_str!(concat!(file!(), ":", ::core::line!(), ": My CStr!"));
/// ```
#[macro_export]
macro_rules! c_str {
diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs
index d18fbf4daa0a..8cc9a20f470e 100644
--- a/rust/macros/helpers.rs
+++ b/rust/macros/helpers.rs
@@ -38,22 +38,6 @@ impl AsciiLitStr {
}
}
-pub(crate) fn file() -> String {
- #[cfg(not(CONFIG_RUSTC_HAS_SPAN_FILE))]
- {
- proc_macro::Span::call_site()
- .source_file()
- .path()
- .to_string_lossy()
- .into_owned()
- }
-
- #[cfg(CONFIG_RUSTC_HAS_SPAN_FILE)]
- {
- proc_macro::Span::call_site().file()
- }
-}
-
/// Obtain all `#[cfg]` attributes.
pub(crate) fn gather_cfg_attrs(attr: &[Attribute]) -> impl Iterator<Item = &Attribute> + '_ {
attr.iter().filter(|a| a.path().is_ident("cfg"))
diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs
index ae20ed6768f1..e9152b9d51f9 100644
--- a/rust/macros/kunit.rs
+++ b/rust/macros/kunit.rs
@@ -109,12 +109,11 @@ pub(crate) fn kunit_tests(test_suite: Ident, mut module: ItemMod) -> Result<Toke
// Before the test, override usual `assert!` and `assert_eq!` macros with ones that call
// KUnit instead.
let test_str = test.to_string();
- let path = CString::new(crate::helpers::file()).expect("file path cannot contain NUL");
processed_items.push(parse_quote! {
#[allow(unused)]
macro_rules! assert {
($cond:expr $(,)?) => {{
- kernel::kunit_assert!(#test_str, #path, 0, $cond);
+ kernel::kunit_assert!(#test_str, $cond);
}}
}
});
@@ -122,7 +121,7 @@ pub(crate) fn kunit_tests(test_suite: Ident, mut module: ItemMod) -> Result<Toke
#[allow(unused)]
macro_rules! assert_eq {
($left:expr, $right:expr $(,)?) => {{
- kernel::kunit_assert_eq!(#test_str, #path, 0, $left, $right);
+ kernel::kunit_assert_eq!(#test_str, $left, $right);
}}
}
});
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 24f96feaeb34..68386b2ed55f 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -8,11 +8,6 @@
// Stable since Rust 1.87.0.
#![feature(extract_if)]
-//
-// Stable since Rust 1.88.0 under a different name, `proc_macro_span_file`,
-// which was added in Rust 1.88.0. This is why `cfg_attr` is used here, i.e.
-// to avoid depending on the full `proc_macro_span` on Rust >= 1.88.0.
-#![cfg_attr(not(CONFIG_RUSTC_HAS_SPAN_FILE), feature(proc_macro_span))]
mod concat_idents;
mod export;
@@ -129,7 +124,7 @@ use syn::parse_macro_input;
/// # Supported argument types
/// - `type`: type which implements the [`Module`] trait (required).
/// - `name`: ASCII string literal of the name of the kernel module (required).
-/// - `authors`: array of ASCII string literals of the authors of the kernel module.
+/// - `authors`: array of string literals of the authors of the kernel module.
/// - `description`: string literal of the description of the kernel module.
/// - `license`: ASCII string literal of the license of the kernel module (required).
/// - `alias`: array of ASCII string literals of the alias names of the kernel module.
@@ -156,25 +151,15 @@ pub fn module(input: TokenStream) -> TokenStream {
/// associated constant bool for each method in the trait that is set to true if
/// the implementer has overridden the associated method.
///
-/// For a trait method to be optional, it must have a default implementation.
-/// This is also the case for traits annotated with `#[vtable]`, but in this
-/// case the default implementation will never be executed. The reason for this
-/// is that the functions will be called through function pointers installed in
-/// C side vtables. When an optional method is not implemented on a `#[vtable]`
-/// trait, a `NULL` entry is installed in the vtable. Thus the default
-/// implementation is never called. Since these traits are not designed to be
-/// used on the Rust side, it should not be possible to call the default
-/// implementation. This is done to ensure that we call the vtable methods
-/// through the C vtable, and not through the Rust vtable. Therefore, the
-/// default implementation should call `build_error!`, which prevents
-/// calls to this function at compile time:
-///
-/// ```compile_fail
-/// # // Intentionally missing `use`s to simplify `rusttest`.
-/// build_error!(VTABLE_DEFAULT_ERROR)
-/// ```
-///
-/// Note that you might need to import [`kernel::error::VTABLE_DEFAULT_ERROR`].
+/// For a trait method to be optional for normal traits, it must have a default
+/// implementation. However, for many users of `#[vtable]`, the functions will
+/// be called through function pointers installed in C side vtables. When an
+/// optional method is not implemented on a `#[vtable]` trait, a `NULL` entry is
+/// installed in the vtable; thus the default implementation is never called. If
+/// this is the case, `#[optional]` can be applied on the optional method.
+/// Methods annotated as such do not need to be implemented nor need a default
+/// implementation. Calling these methods on types that do not implement them
+/// will fail to build using the `build_error!` mechanism.
///
/// This macro should not be used when all functions are required.
///
@@ -187,7 +172,6 @@ pub fn module(input: TokenStream) -> TokenStream {
/// # Examples
///
/// ```
-/// use kernel::error::VTABLE_DEFAULT_ERROR;
/// use kernel::prelude::*;
///
/// # struct LocalModule;
@@ -204,13 +188,11 @@ pub fn module(input: TokenStream) -> TokenStream {
/// // Declares a `#[vtable]` trait
/// #[vtable]
/// pub trait Operations: Send + Sync + Sized {
-/// fn foo(&self) -> Result<()> {
-/// build_error!(VTABLE_DEFAULT_ERROR)
-/// }
+/// #[optional]
+/// fn foo(&self) -> Result<()>;
///
-/// fn bar(&self) -> Result<()> {
-/// build_error!(VTABLE_DEFAULT_ERROR)
-/// }
+/// #[optional]
+/// fn bar(&self) -> Result<()>;
/// }
///
/// struct Foo;
@@ -228,8 +210,6 @@ pub fn module(input: TokenStream) -> TokenStream {
/// assert_eq!(<Foo as Operations>::HAS_BAR, false);
/// # }
/// ```
-///
-/// [`kernel::error::VTABLE_DEFAULT_ERROR`]: ../kernel/error/constant.VTABLE_DEFAULT_ERROR.html
#[proc_macro_attribute]
pub fn vtable(attr: TokenStream, input: TokenStream) -> TokenStream {
parse_macro_input!(attr as syn::parse::Nothing);
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index bc7027f8dbb2..0a7b9e24c960 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -369,7 +369,7 @@ pub(crate) struct ModuleInfo {
type_: Type,
license: AsciiLitStr,
name: AsciiLitStr,
- authors: Option<Punctuated<AsciiLitStr, Token![,]>>,
+ authors: Option<Punctuated<LitStr, Token![,]>>,
description: Option<LitStr>,
alias: Option<Punctuated<AsciiLitStr, Token![,]>>,
firmware: Option<Punctuated<AsciiLitStr, Token![,]>>,
diff --git a/rust/macros/vtable.rs b/rust/macros/vtable.rs
index be9a5ed8abe5..981ad2e49647 100644
--- a/rust/macros/vtable.rs
+++ b/rust/macros/vtable.rs
@@ -9,7 +9,10 @@ use proc_macro2::{
Ident,
TokenStream, //
};
-use quote::ToTokens;
+use quote::{
+ quote,
+ ToTokens, //
+};
use syn::{
parse_quote,
Error,
@@ -46,7 +49,7 @@ fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> {
});
}
- for item in &item.items {
+ for item in &mut item.items {
if let TraitItem::Fn(fn_item) = item {
let name = &fn_item.sig.ident;
let gen_const_name = Ident::new(
@@ -54,14 +57,44 @@ fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> {
name.span(),
);
+ if fn_item
+ .attrs
+ .extract_if(.., |attr| attr.path().is_ident("optional"))
+ .count()
+ != 0
+ {
+ if let Some(default) = &fn_item.default {
+ Err(Error::new_spanned(
+ default,
+ "`#[optional]` methods must not have a default implementation",
+ ))?;
+ }
+
+ // Optional methods in Rust need a default implementation. Inject one that fails
+ // the build at compile time if not overridden.
+ fn_item.default = Some(parse_quote!({
+ ::kernel::build_assert::build_error!(
+ "This function must not be called, see the `#[vtable]` documentation."
+ );
+ }));
+ // Ensure that the function is never code generated unless used.
+ fn_item.attrs.push(parse_quote!(#[inline]));
+ }
+
// We don't know on the implementation-site whether a method is required or provided
// so we have to generate a const for all methods.
+ // However, hide it for required methods as it will always be true.
+ let doc = if fn_item.default.is_some() {
+ let comment =
+ format!("Indicates if the `{name}` method is overridden by the implementor.");
+ quote!(#[doc = #comment])
+ } else {
+ quote!(#[doc(hidden)])
+ };
let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs);
- let comment =
- format!("Indicates if the `{name}` method is overridden by the implementor.");
gen_items.push(parse_quote! {
#(#cfg_attrs)*
- #[doc = #comment]
+ #doc
const #gen_const_name: bool = false;
});
}
diff --git a/scripts/remove-stale-files b/scripts/remove-stale-files
index 6e39fa8540df..f1f776caae00 100755
--- a/scripts/remove-stale-files
+++ b/scripts/remove-stale-files
@@ -26,3 +26,5 @@ rm -f scripts/selinux/genheaders/genheaders
rm -f *.spec
rm -f lib/test_fortify.log
+
+rm -f rust/doctests_kernel_generated_kunit.c
diff --git a/scripts/rustdoc_test_builder.rs b/scripts/rustdoc_test_builder.rs
index df864437cef7..0361e22853f2 100644
--- a/scripts/rustdoc_test_builder.rs
+++ b/scripts/rustdoc_test_builder.rs
@@ -72,6 +72,13 @@ fn main() {
// Figure out a smaller test name based on the generated function name.
let name = rustdoc_function_name.split_once("_rust_kernel_").unwrap().1;
+ // The `rustdoc` function name can include the absolute path when building with `O=` which is
+ // undesirable and creates overlong symbol names. Remap it to use a relative path.
+ // TODO: Remove when bumping the minimum version since with Rust >= 1.95 we already do the
+ // path remapping via the `rustdoc` `--remap-path-prefix` flag.
+ let trimmed_function_name = format!("_doctest_main_rust_kernel_{name}");
+ let body = body.replace(&rustdoc_function_name, &trimmed_function_name);
+
let path = format!("rust/test/doctests/kernel/{name}");
std::fs::write(path, body.as_bytes()).unwrap();
diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs
index d087c0d9fcb3..754c5aafcce4 100644
--- a/scripts/rustdoc_test_gen.rs
+++ b/scripts/rustdoc_test_gen.rs
@@ -125,9 +125,7 @@ fn main() {
// Sort paths.
paths.sort();
- let mut rust_tests = String::new();
- let mut c_test_declarations = String::new();
- let mut c_test_cases = String::new();
+ let mut tests = String::new();
let mut body = String::new();
let mut last_file = String::new();
let mut number = 0;
@@ -172,28 +170,22 @@ fn main() {
use std::fmt::Write;
write!(
- rust_tests,
+ tests,
r#"/// Generated `{name}` KUnit test case from a Rust documentation test.
-#[no_mangle]
-pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{
- /// Overrides the usual [`assert!`] macro with one that calls KUnit instead.
+#[test]
+fn {kunit_name}() {{
+ /// Overrides the usual [`file!`] macro with one that expands to the real path.
#[allow(unused)]
- macro_rules! assert {{
- ($cond:expr $(,)?) => {{{{
- ::kernel::kunit_assert!(
- "{kunit_name}", c"{real_path}", __DOCTEST_ANCHOR - {line}, $cond
- );
- }}}}
+ macro_rules! file {{
+ () => {{ "{real_path}" }}
}}
- /// Overrides the usual [`assert_eq!`] macro with one that calls KUnit instead.
+ /// Overrides the usual [`line!`] macro with one that expands to the real line number.
#[allow(unused)]
- macro_rules! assert_eq {{
- ($left:expr, $right:expr $(,)?) => {{{{
- ::kernel::kunit_assert_eq!(
- "{kunit_name}", c"{real_path}", __DOCTEST_ANCHOR - {line}, $left, $right
- );
- }}}}
+ macro_rules! line {{
+ // NOTE: This does not expand to a literal, but a constant expression.
+ // Therefore code that depends on `line!()` expanding to a literal needs special adjustment.
+ () => {{ const {{ ::core::line!() - __DOCTEST_ANCHOR + {line} }} }}
}}
// Many tests need the prelude, so provide it by default.
@@ -213,7 +205,7 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{
/// The anchor where the test code body starts.
#[allow(unused)]
- static __DOCTEST_ANCHOR: i32 = ::core::line!() as i32 + {body_offset} + 2;
+ static __DOCTEST_ANCHOR: u32 = ::core::line!() + {body_offset} + 2;
{{
#![allow(unreachable_pub, clippy::disallowed_names)]
{body}
@@ -224,14 +216,9 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{
"#
)
.unwrap();
-
- write!(c_test_declarations, "void {kunit_name}(struct kunit *);\n").unwrap();
- write!(c_test_cases, " KUNIT_CASE({kunit_name}),\n").unwrap();
}
- let rust_tests = rust_tests.trim();
- let c_test_declarations = c_test_declarations.trim();
- let c_test_cases = c_test_cases.trim();
+ let tests = tests.trim();
write!(
BufWriter::new(File::create("rust/doctests_kernel_generated.rs").unwrap()),
@@ -257,34 +244,10 @@ impl ModuleMetadata for LocalModule {{
}};
}}
-{rust_tests}
-"#
- )
- .unwrap();
-
- write!(
- BufWriter::new(File::create("rust/doctests_kernel_generated_kunit.c").unwrap()),
- r#"/*
- * `kernel` crate documentation tests.
- */
-
-#include <kunit/test.h>
-
-{c_test_declarations}
-
-static struct kunit_case test_cases[] = {{
- {c_test_cases}
- {{ }}
-}};
-
-static struct kunit_suite test_suite = {{
- .name = "rust_doctests_kernel",
- .test_cases = test_cases,
-}};
-
-kunit_test_suite(test_suite);
-
-MODULE_LICENSE("GPL");
+#[kernel::macros::kunit_tests(rust_doctests_kernel)]
+mod tests {{
+{tests}
+}}
"#
)
.unwrap();