From: Matthew Wood <thepacketgeek@gmail.com>
To: Miguel Ojeda <ojeda@kernel.org>,
Luis Chamberlain <mcgrof@kernel.org>,
Petr Pavlu <petr.pavlu@suse.com>,
Daniel Gomez <da.gomez@kernel.org>,
Sami Tolvanen <samitolvanen@google.com>
Cc: "Aaron Tomlin" <atomlin@atomlin.com>,
"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
"Tamir Duberstein" <tamird@kernel.org>,
"David Gow" <davidgow@google.com>,
"José Expósito" <jose.exposito89@gmail.com>,
linux-modules@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: [PATCH 6/8] rust: macros: add early_param support to module! macro
Date: Thu, 26 Feb 2026 15:47:32 -0800 [thread overview]
Message-ID: <20260226234736.428341-7-thepacketgeek@gmail.com> (raw)
In-Reply-To: <20260226234736.428341-1-thepacketgeek@gmail.com>
Allow module parameters to be registered as early boot command-line
parameters via the existing __setup infrastructure.
Add an optional `early_param` field to the parameter block in the
module! macro. When specified, the macro generates:
- A setup callback that calls ModuleParam::from_setup_arg() and
stores the result via ModuleParamAccess::set_value().
- A static string in .init.rodata with the command-line prefix.
- An ObsKernelParam entry in the .init.setup section linking the
two together.
The generated code is gated behind #[cfg(not(MODULE))] since __setup
parameters are only meaningful for built-in modules — loadable modules
receive their parameters through the standard module_param mechanism.
Signed-off-by: Matthew Wood <thepacketgeek@gmail.com>
---
rust/macros/lib.rs | 5 +++
rust/macros/module.rs | 81 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 86 insertions(+)
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index 0c36194d9971..83dcc89a425a 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -35,9 +35,14 @@
/// parameter_name: type {
/// default: default_value,
/// description: "Description",
+/// early_param: "cmdline_prefix=", // optional
/// }
/// ```
///
+/// The optional `early_param` field registers the parameter as an early
+/// boot command-line parameter via `__setup`. The value is the command-line
+/// prefix string (e.g. `"netconsole="`).
+///
/// `type` may be one of
///
/// - [`i8`]
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index 0d76743741fb..4d2e144fa6de 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -38,6 +38,7 @@ struct ModInfoBuilder<'a> {
counter: usize,
ts: TokenStream,
param_ts: TokenStream,
+ setup_ts: TokenStream,
}
impl<'a> ModInfoBuilder<'a> {
@@ -47,6 +48,7 @@ fn new(module: &'a str) -> Self {
counter: 0,
ts: TokenStream::new(),
param_ts: TokenStream::new(),
+ setup_ts: TokenStream::new(),
}
}
@@ -204,6 +206,78 @@ fn emit_params(&mut self, info: &ModuleInfo) {
});
}
}
+
+ fn emit_setup(&mut self, info: &ModuleInfo) {
+ let Some(params) = &info.params else {
+ return;
+ };
+
+ for param in params {
+ let Some(early_param) = ¶m.early_param else {
+ continue;
+ };
+
+ let setup_str_value = early_param.value();
+ let param_name = ¶m.name;
+
+ // Resolve `string` shorthand to the real `StringParam` type.
+ let param_type_str = param.ptype.to_token_stream().to_string();
+ let actual_type: Type = if param_type_str == "string" {
+ parse_quote!(::kernel::module_param::StringParam)
+ } else {
+ param.ptype.clone()
+ };
+
+ // Create identifiers for the generated statics
+ let setup_fn_name = format_ident!("__setup_fn_{}", param_name);
+ let setup_str_name =
+ format_ident!("__SETUP_STR_{}", param_name.to_string().to_uppercase());
+ let setup_name = format_ident!("__SETUP_{}", param_name.to_string().to_uppercase());
+
+ // The setup string with null terminator
+ let setup_str_bytes = format!("{}\0", setup_str_value);
+ let setup_str_len = setup_str_bytes.len();
+ let setup_str_lit = Literal::byte_string(setup_str_bytes.as_bytes());
+
+ self.setup_ts.extend(quote! {
+ #[cfg(not(MODULE))]
+ const _: () = {
+ unsafe extern "C" fn #setup_fn_name(
+ val: *mut ::kernel::ffi::c_char,
+ ) -> ::kernel::ffi::c_int {
+ if val.is_null() {
+ return 0;
+ }
+ // SAFETY: The kernel passes a valid null-terminated string from
+ // `static_command_line`.
+ match unsafe {
+ <#actual_type as ::kernel::module_param::ModuleParam>::from_setup_arg(
+ val as *const _
+ )
+ } {
+ Ok(v) => {
+ if module_parameters::#param_name.set_value(v) { 1 } else { 0 }
+ }
+ Err(_) => 0,
+ }
+ }
+
+ #[link_section = ".init.rodata"]
+ #[used(compiler)]
+ static #setup_str_name: [u8; #setup_str_len] = *#setup_str_lit;
+
+ #[link_section = ".init.setup"]
+ #[used(compiler)]
+ static #setup_name: ::kernel::module_param::ObsKernelParam =
+ ::kernel::module_param::ObsKernelParam {
+ str_: #setup_str_name.as_ptr().cast(),
+ setup_func: ::core::option::Option::Some(#setup_fn_name),
+ early: 0,
+ };
+ };
+ });
+ }
+ }
}
fn param_ops_path(param_type: &str) -> Path {
@@ -367,6 +441,7 @@ struct Parameter {
ptype: Type,
default: Expr,
description: LitStr,
+ early_param: Option<LitStr>,
}
impl Parse for Parameter {
@@ -382,6 +457,7 @@ fn parse(input: ParseStream<'_>) -> Result<Self> {
from fields;
default [required] => fields.parse()?,
description [required] => fields.parse()?,
+ early_param => fields.parse()?,
}
Ok(Self {
@@ -389,6 +465,7 @@ fn parse(input: ParseStream<'_>) -> Result<Self> {
ptype,
default,
description,
+ early_param,
})
}
}
@@ -501,9 +578,11 @@ pub(crate) fn module(info: ModuleInfo) -> Result<TokenStream> {
modinfo.emit_only_builtin("file", &file, false);
modinfo.emit_params(&info);
+ modinfo.emit_setup(&info);
let modinfo_ts = modinfo.ts;
let params_ts = modinfo.param_ts;
+ let setup_ts = modinfo.setup_ts;
let ident_init = format_ident!("__{ident}_init");
let ident_exit = format_ident!("__{ident}_exit");
@@ -678,5 +757,7 @@ unsafe fn __exit() {
mod module_parameters {
#params_ts
}
+
+ #setup_ts
})
}
--
2.52.0
next prev parent reply other threads:[~2026-02-26 23:47 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-26 23:47 [PATCH 0/8] rust: module parameter extensions Matthew Wood
2026-02-26 23:47 ` [PATCH 1/8] rust: module_param: add StringParam type for C string parameters Matthew Wood
2026-02-28 1:32 ` Miguel Ojeda
2026-03-05 12:47 ` Petr Pavlu
2026-02-26 23:47 ` [PATCH 2/8] rust: module_param: wire StringParam into the module! macro Matthew Wood
2026-03-04 8:13 ` Petr Pavlu
2026-03-09 2:24 ` Matthew Wood
2026-03-06 19:27 ` Sami Tolvanen
2026-03-09 2:27 ` Matthew Wood
2026-02-26 23:47 ` [PATCH 3/8] samples: rust_minimal: demonstrate string module parameter Matthew Wood
2026-02-26 23:47 ` [PATCH 4/8] rust: module_param: add ObsKernelParam type Matthew Wood
2026-02-26 23:47 ` [PATCH 5/8] rust: module_param: add from_setup_arg() to ModuleParam trait Matthew Wood
2026-02-26 23:47 ` Matthew Wood [this message]
2026-03-06 17:22 ` [PATCH 6/8] rust: macros: add early_param support to module! macro Petr Pavlu
2026-02-26 23:47 ` [PATCH 7/8] samples: rust_minimal: demonstrate early_param usage Matthew Wood
2026-02-26 23:47 ` [PATCH 8/8] rust: macros: add configurable initcall levels to module! macro Matthew Wood
2026-02-27 13:27 ` [PATCH 0/8] rust: module parameter extensions Matthew Wood
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260226234736.428341-7-thepacketgeek@gmail.com \
--to=thepacketgeek@gmail.com \
--cc=a.hindborg@kernel.org \
--cc=aliceryhl@google.com \
--cc=atomlin@atomlin.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=da.gomez@kernel.org \
--cc=dakr@kernel.org \
--cc=davidgow@google.com \
--cc=gary@garyguo.net \
--cc=jose.exposito89@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-modules@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=mcgrof@kernel.org \
--cc=ojeda@kernel.org \
--cc=petr.pavlu@suse.com \
--cc=rust-for-linux@vger.kernel.org \
--cc=samitolvanen@google.com \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox