[PATCH v14 5/7] rust: module: update the module macro with module parameter support

Andreas Hindborg posted 7 patches 3 months, 1 week ago
There is a newer version of this series
[PATCH v14 5/7] rust: module: update the module macro with module parameter support
Posted by Andreas Hindborg 3 months, 1 week ago
Allow module parameters to be declared in the rust `module!` macro.

Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
---
 rust/macros/helpers.rs |  25 +++++++
 rust/macros/lib.rs     |  31 +++++++++
 rust/macros/module.rs  | 177 ++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 223 insertions(+), 10 deletions(-)

diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs
index e2602be402c1..365d7eb499c0 100644
--- a/rust/macros/helpers.rs
+++ b/rust/macros/helpers.rs
@@ -10,6 +10,17 @@ pub(crate) fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
     }
 }
 
+pub(crate) fn try_sign(it: &mut token_stream::IntoIter) -> Option<char> {
+    let peek = it.clone().next();
+    match peek {
+        Some(TokenTree::Punct(punct)) if punct.as_char() == '-' => {
+            let _ = it.next();
+            Some(punct.as_char())
+        }
+        _ => None,
+    }
+}
+
 pub(crate) fn try_literal(it: &mut token_stream::IntoIter) -> Option<String> {
     if let Some(TokenTree::Literal(literal)) = it.next() {
         Some(literal.to_string())
@@ -103,3 +114,17 @@ pub(crate) fn file() -> String {
         proc_macro::Span::call_site().file()
     }
 }
+
+/// Parse a token stream of the form `expected_name: "value",` and return the
+/// string in the position of "value".
+///
+/// # Panics
+///
+/// - On parse error.
+pub(crate) fn expect_string_field(it: &mut token_stream::IntoIter, expected_name: &str) -> String {
+    assert_eq!(expect_ident(it), expected_name);
+    assert_eq!(expect_punct(it), ':');
+    let string = expect_string(it);
+    assert_eq!(expect_punct(it), ',');
+    string
+}
diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
index fa847cf3a9b5..2fb520dc930a 100644
--- a/rust/macros/lib.rs
+++ b/rust/macros/lib.rs
@@ -28,6 +28,30 @@
 /// The `type` argument should be a type which implements the [`Module`]
 /// trait. Also accepts various forms of kernel metadata.
 ///
+/// The `params` field describe module parameters. Each entry has the form
+///
+/// ```ignore
+/// parameter_name: type {
+///     default: default_value,
+///     description: "Description",
+/// }
+/// ```
+///
+/// `type` may be one of
+///
+/// - [`i8`]
+/// - [`u8`]
+/// - [`i8`]
+/// - [`u8`]
+/// - [`i16`]
+/// - [`u16`]
+/// - [`i32`]
+/// - [`u32`]
+/// - [`i64`]
+/// - [`u64`]
+/// - [`isize`]
+/// - [`usize`]
+///
 /// C header: [`include/linux/moduleparam.h`](srctree/include/linux/moduleparam.h)
 ///
 /// [`Module`]: ../kernel/trait.Module.html
@@ -44,6 +68,12 @@
 ///     description: "My very own kernel module!",
 ///     license: "GPL",
 ///     alias: ["alternate_module_name"],
+///     params: {
+///         my_parameter: i64 {
+///             default: 1,
+///             description: "This parameter has a default of 1",
+///         },
+///     },
 /// }
 ///
 /// struct MyModule(i32);
@@ -52,6 +82,7 @@
 ///     fn init(_module: &'static ThisModule) -> Result<Self> {
 ///         let foo: i32 = 42;
 ///         pr_info!("I contain:  {}\n", foo);
+///         pr_info!("i32 param is:  {}\n", module_parameters::my_parameter.read());
 ///         Ok(Self(foo))
 ///     }
 /// }
diff --git a/rust/macros/module.rs b/rust/macros/module.rs
index 1a867a1e787e..2b8123fe9fb0 100644
--- a/rust/macros/module.rs
+++ b/rust/macros/module.rs
@@ -26,6 +26,7 @@ struct ModInfoBuilder<'a> {
     module: &'a str,
     counter: usize,
     buffer: String,
+    param_buffer: String,
 }
 
 impl<'a> ModInfoBuilder<'a> {
@@ -34,10 +35,11 @@ fn new(module: &'a str) -> Self {
             module,
             counter: 0,
             buffer: String::new(),
+            param_buffer: String::new(),
         }
     }
 
-    fn emit_base(&mut self, field: &str, content: &str, builtin: bool) {
+    fn emit_base(&mut self, field: &str, content: &str, builtin: bool, param: bool) {
         let string = if builtin {
             // Built-in modules prefix their modinfo strings by `module.`.
             format!(
@@ -51,8 +53,14 @@ fn emit_base(&mut self, field: &str, content: &str, builtin: bool) {
             format!("{field}={content}\0")
         };
 
+        let buffer = if param {
+            &mut self.param_buffer
+        } else {
+            &mut self.buffer
+        };
+
         write!(
-            &mut self.buffer,
+            buffer,
             "
                 {cfg}
                 #[doc(hidden)]
@@ -75,20 +83,118 @@ fn emit_base(&mut self, field: &str, content: &str, builtin: bool) {
         self.counter += 1;
     }
 
-    fn emit_only_builtin(&mut self, field: &str, content: &str) {
-        self.emit_base(field, content, true)
+    fn emit_only_builtin(&mut self, field: &str, content: &str, param: bool) {
+        self.emit_base(field, content, true, param)
     }
 
-    fn emit_only_loadable(&mut self, field: &str, content: &str) {
-        self.emit_base(field, content, false)
+    fn emit_only_loadable(&mut self, field: &str, content: &str, param: bool) {
+        self.emit_base(field, content, false, param)
     }
 
     fn emit(&mut self, field: &str, content: &str) {
-        self.emit_only_builtin(field, content);
-        self.emit_only_loadable(field, content);
+        self.emit_internal(field, content, false);
+    }
+
+    fn emit_internal(&mut self, field: &str, content: &str, param: bool) {
+        self.emit_only_builtin(field, content, param);
+        self.emit_only_loadable(field, content, param);
+    }
+
+    fn emit_param(&mut self, field: &str, param: &str, content: &str) {
+        let content = format!("{param}:{content}", param = param, content = content);
+        self.emit_internal(field, &content, true);
+    }
+
+    fn emit_params(&mut self, info: &ModuleInfo) {
+        let Some(params) = &info.params else {
+            return;
+        };
+
+        for param in params {
+            let ops = param_ops_path(&param.ptype);
+
+            // Note: The spelling of these fields is dictated by the user space
+            // tool `modinfo`.
+            self.emit_param("parmtype", &param.name, &param.ptype);
+            self.emit_param("parm", &param.name, &param.description);
+
+            write!(
+                self.param_buffer,
+                "
+                    pub(crate) static {param_name}:
+                        ::kernel::module_param::ModuleParamAccess<{param_type}> =
+                            ::kernel::module_param::ModuleParamAccess::new({param_default});
+
+                    #[link_section = \"__param\"]
+                    #[used]
+                    static __{module_name}_{param_name}_struct:
+                        ::kernel::module_param::RacyKernelParam =
+                        ::kernel::module_param::RacyKernelParam::new(
+                          ::kernel::bindings::kernel_param {{
+                            name: if cfg!(MODULE) {{
+                                ::kernel::c_str!(\"{param_name}\").as_bytes_with_nul()
+                            }} else {{
+                                ::kernel::c_str!(\"{module_name}.{param_name}\").as_bytes_with_nul()
+                            }}.as_ptr(),
+                            // SAFETY: `__this_module` is constructed by the kernel at load time
+                            // and will not be freed until the module is unloaded.
+                            #[cfg(MODULE)]
+                            mod_: unsafe {{
+                                (&::kernel::bindings::__this_module
+                                    as *const ::kernel::bindings::module)
+                                    .cast_mut()
+                            }},
+                            #[cfg(not(MODULE))]
+                            mod_: ::core::ptr::null_mut(),
+                            ops: &{ops} as *const ::kernel::bindings::kernel_param_ops,
+                            perm: 0, // Will not appear in sysfs
+                            level: -1,
+                            flags: 0,
+                            __bindgen_anon_1:
+                                ::kernel::bindings::kernel_param__bindgen_ty_1 {{
+                                    arg: {param_name}.as_void_ptr()
+                                }},
+                          }}
+                        );
+                ",
+                module_name = info.name,
+                param_type = param.ptype,
+                param_default = param.default,
+                param_name = param.name,
+                ops = ops,
+            )
+            .unwrap();
+        }
+    }
+}
+
+fn param_ops_path(param_type: &str) -> &'static str {
+    match param_type {
+        "i8" => "::kernel::module_param::PARAM_OPS_I8",
+        "u8" => "::kernel::module_param::PARAM_OPS_U8",
+        "i16" => "::kernel::module_param::PARAM_OPS_I16",
+        "u16" => "::kernel::module_param::PARAM_OPS_U16",
+        "i32" => "::kernel::module_param::PARAM_OPS_I32",
+        "u32" => "::kernel::module_param::PARAM_OPS_U32",
+        "i64" => "::kernel::module_param::PARAM_OPS_I64",
+        "u64" => "::kernel::module_param::PARAM_OPS_U64",
+        "isize" => "::kernel::module_param::PARAM_OPS_ISIZE",
+        "usize" => "::kernel::module_param::PARAM_OPS_USIZE",
+        t => panic!("Unsupported parameter type {}", t),
     }
 }
 
+fn expect_param_default(param_it: &mut token_stream::IntoIter) -> String {
+    assert_eq!(expect_ident(param_it), "default");
+    assert_eq!(expect_punct(param_it), ':');
+    let sign = try_sign(param_it);
+    let default = try_literal(param_it).expect("Expected default param value");
+    assert_eq!(expect_punct(param_it), ',');
+    let mut value = sign.map(String::from).unwrap_or_default();
+    value.push_str(&default);
+    value
+}
+
 #[derive(Debug, Default)]
 struct ModuleInfo {
     type_: String,
@@ -99,6 +205,50 @@ struct ModuleInfo {
     description: Option<String>,
     alias: Option<Vec<String>>,
     firmware: Option<Vec<String>>,
+    params: Option<Vec<Parameter>>,
+}
+
+#[derive(Debug)]
+struct Parameter {
+    name: String,
+    ptype: String,
+    default: String,
+    description: String,
+}
+
+fn expect_params(it: &mut token_stream::IntoIter) -> Vec<Parameter> {
+    let params = expect_group(it);
+    assert_eq!(params.delimiter(), Delimiter::Brace);
+    let mut it = params.stream().into_iter();
+    let mut parsed = Vec::new();
+
+    loop {
+        let param_name = match it.next() {
+            Some(TokenTree::Ident(ident)) => ident.to_string(),
+            Some(_) => panic!("Expected Ident or end"),
+            None => break,
+        };
+
+        assert_eq!(expect_punct(&mut it), ':');
+        let param_type = expect_ident(&mut it);
+        let group = expect_group(&mut it);
+        assert_eq!(group.delimiter(), Delimiter::Brace);
+        assert_eq!(expect_punct(&mut it), ',');
+
+        let mut param_it = group.stream().into_iter();
+        let param_default = expect_param_default(&mut param_it);
+        let param_description = expect_string_field(&mut param_it, "description");
+        expect_end(&mut param_it);
+
+        parsed.push(Parameter {
+            name: param_name,
+            ptype: param_type,
+            default: param_default,
+            description: param_description,
+        })
+    }
+
+    parsed
 }
 
 impl ModuleInfo {
@@ -114,6 +264,7 @@ fn parse(it: &mut token_stream::IntoIter) -> Self {
             "license",
             "alias",
             "firmware",
+            "params",
         ];
         const REQUIRED_KEYS: &[&str] = &["type", "name", "license"];
         let mut seen_keys = Vec::new();
@@ -140,6 +291,7 @@ fn parse(it: &mut token_stream::IntoIter) -> Self {
                 "license" => info.license = expect_string_ascii(it),
                 "alias" => info.alias = Some(expect_string_array(it)),
                 "firmware" => info.firmware = Some(expect_string_array(it)),
+                "params" => info.params = Some(expect_params(it)),
                 _ => panic!("Unknown key \"{key}\". Valid keys are: {EXPECTED_KEYS:?}."),
             }
 
@@ -205,7 +357,9 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream {
     // Built-in modules also export the `file` modinfo string.
     let file =
         std::env::var("RUST_MODFILE").expect("Unable to fetch RUST_MODFILE environmental variable");
-    modinfo.emit_only_builtin("file", &file);
+    modinfo.emit_only_builtin("file", &file, false);
+
+    modinfo.emit_params(&info);
 
     format!(
         "
@@ -369,15 +523,18 @@ unsafe fn __exit() {{
                             __MOD.assume_init_drop();
                         }}
                     }}
-
                     {modinfo}
                 }}
             }}
+            mod module_parameters {{
+                {params}
+            }}
         ",
         type_ = info.type_,
         name = info.name,
         ident = ident,
         modinfo = modinfo.buffer,
+        params = modinfo.param_buffer,
         initcall_section = ".initcall6.init"
     )
     .parse()

-- 
2.47.2
Re: [PATCH v14 5/7] rust: module: update the module macro with module parameter support
Posted by Benno Lossin 3 months, 1 week ago
On Wed Jul 2, 2025 at 3:18 PM CEST, Andreas Hindborg wrote:
> Allow module parameters to be declared in the rust `module!` macro.
>
> Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>

A few nits below, with those fixed

Reviewed-by: Benno Lossin <lossin@kernel.org>

> ---
>  rust/macros/helpers.rs |  25 +++++++
>  rust/macros/lib.rs     |  31 +++++++++
>  rust/macros/module.rs  | 177 ++++++++++++++++++++++++++++++++++++++++++++++---
>  3 files changed, 223 insertions(+), 10 deletions(-)

> +    fn emit_params(&mut self, info: &ModuleInfo) {
> +        let Some(params) = &info.params else {
> +            return;
> +        };
> +
> +        for param in params {
> +            let ops = param_ops_path(&param.ptype);
> +
> +            // Note: The spelling of these fields is dictated by the user space
> +            // tool `modinfo`.
> +            self.emit_param("parmtype", &param.name, &param.ptype);
> +            self.emit_param("parm", &param.name, &param.description);
> +
> +            write!(
> +                self.param_buffer,
> +                "
> +                    pub(crate) static {param_name}:
> +                        ::kernel::module_param::ModuleParamAccess<{param_type}> =
> +                            ::kernel::module_param::ModuleParamAccess::new({param_default});
> +
> +                    #[link_section = \"__param\"]
> +                    #[used]
> +                    static __{module_name}_{param_name}_struct:

Does it make sense to move this static to a `const _: () = {};` block?

> +                        ::kernel::module_param::RacyKernelParam =
> +                        ::kernel::module_param::RacyKernelParam::new(
> +                          ::kernel::bindings::kernel_param {{
> +                            name: if cfg!(MODULE) {{

s/cfg/::core::cfg/

:)

Also there seems to only be a 2-space indentation here.

> +                                ::kernel::c_str!(\"{param_name}\").as_bytes_with_nul()
> +                            }} else {{
> +                                ::kernel::c_str!(\"{module_name}.{param_name}\").as_bytes_with_nul()
> +                            }}.as_ptr(),
> +                            // SAFETY: `__this_module` is constructed by the kernel at load time
> +                            // and will not be freed until the module is unloaded.
> +                            #[cfg(MODULE)]
> +                            mod_: unsafe {{
> +                                (&::kernel::bindings::__this_module
> +                                    as *const ::kernel::bindings::module)
> +                                    .cast_mut()
> +                            }},

It doesn't stop with the improvements...

    https://github.com/Rust-for-Linux/linux/issues/1176

Maybe we should also have one to use it here, but eh we can do that
later (and it's not as bad to forget about :)

> +                            #[cfg(not(MODULE))]
> +                            mod_: ::core::ptr::null_mut(),
> +                            ops: &{ops} as *const ::kernel::bindings::kernel_param_ops,

    ::core::ptr::from_ref(&{ops})

> +                            perm: 0, // Will not appear in sysfs
> +                            level: -1,
> +                            flags: 0,
> +                            __bindgen_anon_1:
> +                                ::kernel::bindings::kernel_param__bindgen_ty_1 {{
> +                                    arg: {param_name}.as_void_ptr()
> +                                }},

Formatting?

+                            __bindgen_anon_1: ::kernel::bindings::kernel_param__bindgen_ty_1 {{
+                                arg: {param_name}.as_void_ptr()
+                            }},

---
Cheers,
Benno

> +                          }}
> +                        );
> +                ",
> +                module_name = info.name,
> +                param_type = param.ptype,
> +                param_default = param.default,
> +                param_name = param.name,
> +                ops = ops,
> +            )
> +            .unwrap();
> +        }
> +    }
> +}
Re: [PATCH v14 5/7] rust: module: update the module macro with module parameter support
Posted by Andreas Hindborg 3 months ago
"Benno Lossin" <lossin@kernel.org> writes:

> On Wed Jul 2, 2025 at 3:18 PM CEST, Andreas Hindborg wrote:
>> Allow module parameters to be declared in the rust `module!` macro.
>>
>> Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
>
> A few nits below, with those fixed
>
> Reviewed-by: Benno Lossin <lossin@kernel.org>
>
>> ---
>>  rust/macros/helpers.rs |  25 +++++++
>>  rust/macros/lib.rs     |  31 +++++++++
>>  rust/macros/module.rs  | 177 ++++++++++++++++++++++++++++++++++++++++++++++---
>>  3 files changed, 223 insertions(+), 10 deletions(-)
>
>> +    fn emit_params(&mut self, info: &ModuleInfo) {
>> +        let Some(params) = &info.params else {
>> +            return;
>> +        };
>> +
>> +        for param in params {
>> +            let ops = param_ops_path(&param.ptype);
>> +
>> +            // Note: The spelling of these fields is dictated by the user space
>> +            // tool `modinfo`.
>> +            self.emit_param("parmtype", &param.name, &param.ptype);
>> +            self.emit_param("parm", &param.name, &param.description);
>> +
>> +            write!(
>> +                self.param_buffer,
>> +                "
>> +                    pub(crate) static {param_name}:
>> +                        ::kernel::module_param::ModuleParamAccess<{param_type}> =
>> +                            ::kernel::module_param::ModuleParamAccess::new({param_default});
>> +
>> +                    #[link_section = \"__param\"]
>> +                    #[used]
>> +                    static __{module_name}_{param_name}_struct:
>
> Does it make sense to move this static to a `const _: () = {};` block?

Yes, that makes sense.

>
>> +                        ::kernel::module_param::RacyKernelParam =
>> +                        ::kernel::module_param::RacyKernelParam::new(
>> +                          ::kernel::bindings::kernel_param {{
>> +                            name: if cfg!(MODULE) {{
>
> s/cfg/::core::cfg/

OK.

>
> :)
>
> Also there seems to only be a 2-space indentation here.

Fixed.

>
>> +                                ::kernel::c_str!(\"{param_name}\").as_bytes_with_nul()
>> +                            }} else {{
>> +                                ::kernel::c_str!(\"{module_name}.{param_name}\").as_bytes_with_nul()
>> +                            }}.as_ptr(),
>> +                            // SAFETY: `__this_module` is constructed by the kernel at load time
>> +                            // and will not be freed until the module is unloaded.
>> +                            #[cfg(MODULE)]
>> +                            mod_: unsafe {{
>> +                                (&::kernel::bindings::__this_module
>> +                                    as *const ::kernel::bindings::module)
>> +                                    .cast_mut()
>> +                            }},
>
> It doesn't stop with the improvements...
>
>     https://github.com/Rust-for-Linux/linux/issues/1176
>
> Maybe we should also have one to use it here, but eh we can do that
> later (and it's not as bad to forget about :)

Applying `from_ref`.

>
>> +                            #[cfg(not(MODULE))]
>> +                            mod_: ::core::ptr::null_mut(),
>> +                            ops: &{ops} as *const ::kernel::bindings::kernel_param_ops,
>
>     ::core::ptr::from_ref(&{ops})

👍

>
>> +                            perm: 0, // Will not appear in sysfs
>> +                            level: -1,
>> +                            flags: 0,
>> +                            __bindgen_anon_1:
>> +                                ::kernel::bindings::kernel_param__bindgen_ty_1 {{
>> +                                    arg: {param_name}.as_void_ptr()
>> +                                }},
>
> Formatting?
>
> +                            __bindgen_anon_1: ::kernel::bindings::kernel_param__bindgen_ty_1 {{
> +                                arg: {param_name}.as_void_ptr()
> +                            }},


That makes the line more than 100 characters after changing other
formatting things. Perhaps I should just left shift all this?


Best regards,
Andreas Hindborg
Re: [PATCH v14 5/7] rust: module: update the module macro with module parameter support
Posted by Benno Lossin 3 months ago
On Fri Jul 4, 2025 at 2:29 PM CEST, Andreas Hindborg wrote:
> "Benno Lossin" <lossin@kernel.org> writes:
>> On Wed Jul 2, 2025 at 3:18 PM CEST, Andreas Hindborg wrote:
>>> +                            perm: 0, // Will not appear in sysfs
>>> +                            level: -1,
>>> +                            flags: 0,
>>> +                            __bindgen_anon_1:
>>> +                                ::kernel::bindings::kernel_param__bindgen_ty_1 {{
>>> +                                    arg: {param_name}.as_void_ptr()
>>> +                                }},
>>
>> Formatting?
>>
>> +                            __bindgen_anon_1: ::kernel::bindings::kernel_param__bindgen_ty_1 {{
>> +                                arg: {param_name}.as_void_ptr()
>> +                            }},
>
>
> That makes the line more than 100 characters after changing other
> formatting things. Perhaps I should just left shift all this?

Not sure what you mean by left shift? When I tried it, it was fine, but
it could have changed with the other things... Do you have a branch with
your changes?

---
Cheers,
Benno
Re: [PATCH v14 5/7] rust: module: update the module macro with module parameter support
Posted by Andreas Hindborg 3 months ago
"Benno Lossin" <lossin@kernel.org> writes:

> On Fri Jul 4, 2025 at 2:29 PM CEST, Andreas Hindborg wrote:
>> "Benno Lossin" <lossin@kernel.org> writes:
>>> On Wed Jul 2, 2025 at 3:18 PM CEST, Andreas Hindborg wrote:
>>>> +                            perm: 0, // Will not appear in sysfs
>>>> +                            level: -1,
>>>> +                            flags: 0,
>>>> +                            __bindgen_anon_1:
>>>> +                                ::kernel::bindings::kernel_param__bindgen_ty_1 {{
>>>> +                                    arg: {param_name}.as_void_ptr()
>>>> +                                }},
>>>
>>> Formatting?
>>>
>>> +                            __bindgen_anon_1: ::kernel::bindings::kernel_param__bindgen_ty_1 {{
>>> +                                arg: {param_name}.as_void_ptr()
>>> +                            }},
>>
>>
>> That makes the line more than 100 characters after changing other
>> formatting things. Perhaps I should just left shift all this?
>
> Not sure what you mean by left shift? When I tried it, it was fine, but
> it could have changed with the other things... Do you have a branch with
> your changes?

Move all the code template so the least indented start at column 0.

My WIP branch is here [1].


Best regards,
Andreas Hindborg


[1] https://github.com/metaspace/linux/tree/module-params
Re: [PATCH v14 5/7] rust: module: update the module macro with module parameter support
Posted by Benno Lossin 3 months ago
On Fri Jul 4, 2025 at 3:51 PM CEST, Andreas Hindborg wrote:
> "Benno Lossin" <lossin@kernel.org> writes:
>> On Fri Jul 4, 2025 at 2:29 PM CEST, Andreas Hindborg wrote:
>>> "Benno Lossin" <lossin@kernel.org> writes:
>>>> On Wed Jul 2, 2025 at 3:18 PM CEST, Andreas Hindborg wrote:
>>>>> +                            perm: 0, // Will not appear in sysfs
>>>>> +                            level: -1,
>>>>> +                            flags: 0,
>>>>> +                            __bindgen_anon_1:
>>>>> +                                ::kernel::bindings::kernel_param__bindgen_ty_1 {{
>>>>> +                                    arg: {param_name}.as_void_ptr()
>>>>> +                                }},
>>>>
>>>> Formatting?
>>>>
>>>> +                            __bindgen_anon_1: ::kernel::bindings::kernel_param__bindgen_ty_1 {{
>>>> +                                arg: {param_name}.as_void_ptr()
>>>> +                            }},
>>>
>>>
>>> That makes the line more than 100 characters after changing other
>>> formatting things. Perhaps I should just left shift all this?
>>
>> Not sure what you mean by left shift? When I tried it, it was fine, but
>> it could have changed with the other things... Do you have a branch with
>> your changes?
>
> Move all the code template so the least indented start at column 0.
>
> My WIP branch is here [1].

If you dedent the contents of the string once, then everything fits in
100 columns.

---
Cheers,
Benno

> Best regards,
> Andreas Hindborg
>
>
> [1] https://github.com/metaspace/linux/tree/module-params