On Wed, Jan 7, 2026 at 11:59 AM Gary Guo <[email protected]> wrote:
>
> From: Gary Guo <[email protected]>
>
> This has no behavioural change, but is good for maintainability. With
> `quote!`, we're no longer using string templates, so we don't need to
> quote " and {} inside the template anymore. Further more, editors can
> now highlight the code template.
>
> This also improves the robustness as it eliminates the need for string
> quoting and escaping.
>
> Co-developed-by: Benno Lossin <[email protected]>
> Signed-off-by: Benno Lossin <[email protected]>
> Signed-off-by: Gary Guo <[email protected]>
Are there significant changes here? You didn't pick up my tag.
> ---
> rust/macros/module.rs | 558 ++++++++++++++++++++++--------------------
> 1 file changed, 295 insertions(+), 263 deletions(-)
>
> diff --git a/rust/macros/module.rs b/rust/macros/module.rs
> index 6ad7b411ccde4..ba345d672839e 100644
> --- a/rust/macros/module.rs
> +++ b/rust/macros/module.rs
> @@ -1,12 +1,15 @@
> // SPDX-License-Identifier: GPL-2.0
>
> -use std::fmt::Write;
> +use std::ffi::CString;
>
> use proc_macro2::{
> Literal,
> TokenStream, //
> };
> -use quote::ToTokens;
> +use quote::{
> + format_ident,
> + quote, //
> +};
> use syn::{
> braced,
> bracketed,
> @@ -15,11 +18,13 @@
> Parse,
> ParseStream, //
> },
> + parse_quote,
> punctuated::Punctuated,
> Error,
> Expr,
> Ident,
> LitStr,
> + Path,
> Result,
> Token, //
> };
> @@ -29,8 +34,8 @@
> struct ModInfoBuilder<'a> {
> module: &'a str,
> counter: usize,
> - buffer: String,
> - param_buffer: String,
> + ts: TokenStream,
> + param_ts: TokenStream,
> }
>
> impl<'a> ModInfoBuilder<'a> {
> @@ -38,8 +43,8 @@ fn new(module: &'a str) -> Self {
> ModInfoBuilder {
> module,
> counter: 0,
> - buffer: String::new(),
> - param_buffer: String::new(),
> + ts: TokenStream::new(),
> + param_ts: TokenStream::new(),
> }
> }
>
> @@ -56,33 +61,32 @@ fn emit_base(&mut self, field: &str, content: &str,
> builtin: bool, param: bool)
> // Loadable modules' modinfo strings go as-is.
> format!("{field}={content}\0")
> };
> -
> - let buffer = if param {
> - &mut self.param_buffer
> + let length = string.len();
> + let string = Literal::byte_string(string.as_bytes());
> + let cfg = if builtin {
> + quote!(#[cfg(not(MODULE))])
> } else {
> - &mut self.buffer
> + quote!(#[cfg(MODULE)])
> };
>
> - write!(
> - buffer,
> - "
> - {cfg}
> - #[doc(hidden)]
> - #[cfg_attr(not(target_os = \"macos\"), link_section =
> \".modinfo\")]
> - #[used(compiler)]
> - pub static __{module}_{counter}: [u8; {length}] = *{string};
> - ",
> - cfg = if builtin {
> - "#[cfg(not(MODULE))]"
> - } else {
> - "#[cfg(MODULE)]"
> - },
> + let counter = format_ident!(
> + "__{module}_{counter}",
> module = self.module.to_uppercase(),
> - counter = self.counter,
> - length = string.len(),
> - string = Literal::byte_string(string.as_bytes()),
> - )
> - .unwrap();
> + counter = self.counter
> + );
> + let item = quote! {
> + #cfg
> + #[doc(hidden)]
> + #[cfg_attr(not(target_os = "macos"), link_section = ".modinfo")]
> + #[used(compiler)]
> + pub static #counter: [u8; #length] = *#string;
> + };
> +
> + if param {
> + self.param_ts.extend(item);
> + } else {
> + self.ts.extend(item);
> + }
>
> self.counter += 1;
> }
> @@ -115,77 +119,86 @@ fn emit_params(&mut self, info: &ModuleInfo) {
> };
>
> for param in params {
> - let param_name = param.name.to_string();
> - let param_type = param.ptype.to_string();
> - let param_default = param.default.to_token_stream().to_string();
> + let param_name_str = param.name.to_string();
> + let param_type_str = param.ptype.to_string();
This renaming is unfortunately; these variables were just introduced
in the previous patch.
>
> - let ops = param_ops_path(¶m_type);
> + let ops = param_ops_path(¶m_type_str);
>
> // Note: The spelling of these fields is dictated by the user
> space
> // tool `modinfo`.
> - self.emit_param("parmtype", ¶m_name, ¶m_type);
> - self.emit_param("parm", ¶m_name, ¶m.description.value());
> -
> - write!(
> - self.param_buffer,
> - "
> - pub(crate) static {param_name}:
> - ::kernel::module_param::ModuleParamAccess<{param_type}> =
> -
> ::kernel::module_param::ModuleParamAccess::new({param_default});
> -
> - const _: () = {{
> - #[link_section = \"__param\"]
> - #[used]
> - static __{module_name}_{param_name}_struct:
> + self.emit_param("parmtype", ¶m_name_str, ¶m_type_str);
> + self.emit_param("parm", ¶m_name_str,
> ¶m.description.value());
> +
> + let static_name = format_ident!("__{}_{}_struct", self.module,
> param.name);
> + let param_name_cstr = Literal::c_string(
> + &CString::new(param_name_str).expect("name contains
> NUL-terminator"),
> + );
> + let param_name_cstr_with_module = Literal::c_string(
> + &CString::new(format!("{}.{}", self.module, param.name))
> + .expect("name contains NUL-terminator"),
> + );
> +
> + let param_name = ¶m.name;
> + let param_type = ¶m.ptype;
> + let param_default = ¶m.default;
> +
> + self.param_ts.extend(quote!{
> + #[allow(non_upper_case_globals)]
> + pub(crate) static #param_name:
> + ::kernel::module_param::ModuleParamAccess<#param_type> =
> +
> ::kernel::module_param::ModuleParamAccess::new(#param_default);
> +
> + const _: () = {
> + #[allow(non_upper_case_globals)]
> + #[link_section = "__param"]
> + #[used(compiler)]
> + static #static_name:
> ::kernel::module_param::KernelParam =
> ::kernel::module_param::KernelParam::new(
> - ::kernel::bindings::kernel_param {{
> - name: if ::core::cfg!(MODULE) {{
> -
> ::kernel::c_str!(\"{param_name}\").to_bytes_with_nul()
> - }} else {{
> -
> ::kernel::c_str!(\"{module_name}.{param_name}\")
> - .to_bytes_with_nul()
> - }}.as_ptr(),
> + ::kernel::bindings::kernel_param {
> + name:
> kernel::str::as_char_ptr_in_const_context(
> + if ::core::cfg!(MODULE) {
> + #param_name_cstr
> + } else {
> + #param_name_cstr_with_module
> + }
> + ),
> // 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 {{
> + mod_: unsafe {
>
> core::ptr::from_ref(&::kernel::bindings::__this_module)
> .cast_mut()
> - }},
> + },
> #[cfg(not(MODULE))]
> mod_: ::core::ptr::null_mut(),
> - ops: core::ptr::from_ref(&{ops}),
> + 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()
> - }},
> - }}
> + __bindgen_anon_1:
> ::kernel::bindings::kernel_param__bindgen_ty_1 {
> + arg: #param_name.as_void_ptr()
> + },
> + }
> );
> - }};
> - ",
> - module_name = info.name.value(),
> - ops = ops,
> - )
> - .unwrap();
> + };
> + });
> }
> }
> }
>
> -fn param_ops_path(param_type: &str) -> &'static str {
> +fn param_ops_path(param_type: &str) -> Path {
> 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",
> + "i8" => parse_quote!(::kernel::module_param::PARAM_OPS_I8),
> + "u8" => parse_quote!(::kernel::module_param::PARAM_OPS_U8),
> + "i16" => parse_quote!(::kernel::module_param::PARAM_OPS_I16),
> + "u16" => parse_quote!(::kernel::module_param::PARAM_OPS_U16),
> + "i32" => parse_quote!(::kernel::module_param::PARAM_OPS_I32),
> + "u32" => parse_quote!(::kernel::module_param::PARAM_OPS_U32),
> + "i64" => parse_quote!(::kernel::module_param::PARAM_OPS_I64),
> + "u64" => parse_quote!(::kernel::module_param::PARAM_OPS_U64),
> + "isize" => parse_quote!(::kernel::module_param::PARAM_OPS_ISIZE),
> + "usize" => parse_quote!(::kernel::module_param::PARAM_OPS_USIZE),
> t => panic!("Unsupported parameter type {}", t),
> }
> }
> @@ -424,29 +437,41 @@ fn parse(input: ParseStream<'_>) -> Result<Self> {
> }
>
> pub(crate) fn module(info: ModuleInfo) -> Result<TokenStream> {
> + let ModuleInfo {
> + type_,
> + license,
> + name,
> + authors,
> + description,
> + alias,
> + firmware,
> + imports_ns,
> + params: _,
> + } = &info;
> +
> // Rust does not allow hyphens in identifiers, use underscore instead.
> - let ident = info.name.value().replace('-', "_");
> + let ident = name.value().replace('-', "_");
> let mut modinfo = ModInfoBuilder::new(ident.as_ref());
> - if let Some(authors) = &info.authors {
> + if let Some(authors) = authors {
> for author in authors {
> modinfo.emit("author", &author.value());
> }
> }
> - if let Some(description) = &info.description {
> + if let Some(description) = description {
> modinfo.emit("description", &description.value());
> }
> - modinfo.emit("license", &info.license.value());
> - if let Some(aliases) = &info.alias {
> + modinfo.emit("license", &license.value());
> + if let Some(aliases) = alias {
> for alias in aliases {
> modinfo.emit("alias", &alias.value());
> }
> }
> - if let Some(firmware) = &info.firmware {
> + if let Some(firmware) = firmware {
> for fw in firmware {
> modinfo.emit("firmware", &fw.value());
> }
> }
> - if let Some(imports) = &info.imports_ns {
> + if let Some(imports) = imports_ns {
> for ns in imports {
> modinfo.emit("import_ns", &ns.value());
> }
> @@ -459,182 +484,189 @@ pub(crate) fn module(info: ModuleInfo) ->
> Result<TokenStream> {
>
> modinfo.emit_params(&info);
>
> - Ok(format!(
> - "
> - /// The module name.
> - ///
> - /// Used by the printing macros, e.g. [`info!`].
> - const __LOG_PREFIX: &[u8] = b\"{name}\\0\";
> -
> - // 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_};
> -
> - impl ::kernel::ModuleMetadata for {type_} {{
> - const NAME: &'static ::kernel::str::CStr = c\"{name}\";
> - }}
> -
> - // Double nested modules, since then nobody can access the
> public items inside.
> - mod __module_init {{
> - mod __module_init {{
> - use super::super::{type_};
> - use pin_init::PinInit;
> -
> - /// The \"Rust loadable module\" mark.
> - //
> - // This may be best done another way later on, e.g. as a
> new modinfo
> - // key or a new section. For the moment, keep it simple.
> - #[cfg(MODULE)]
> - #[doc(hidden)]
> - #[used(compiler)]
> - static __IS_RUST_MODULE: () = ();
> -
> - static mut __MOD: ::core::mem::MaybeUninit<{type_}> =
> - ::core::mem::MaybeUninit::uninit();
> -
> - // Loadable modules need to export the
> `{{init,cleanup}}_module` identifiers.
> - /// # Safety
> - ///
> - /// This function must not be called after module
> initialization, because it may be
> - /// freed after that completes.
> - #[cfg(MODULE)]
> - #[doc(hidden)]
> - #[no_mangle]
> - #[link_section = \".init.text\"]
> - pub unsafe extern \"C\" fn init_module() ->
> ::kernel::ffi::c_int {{
> - // SAFETY: This function is inaccessible to the
> outside due to the double
> - // module wrapping it. It is called exactly once by
> the C side via its
> - // unique name.
> - unsafe {{ __init() }}
> - }}
> -
> - #[cfg(MODULE)]
> - #[doc(hidden)]
> - #[used(compiler)]
> - #[link_section = \".init.data\"]
> - static __UNIQUE_ID___addressable_init_module: unsafe
> extern \"C\" fn() -> i32 = init_module;
> -
> - #[cfg(MODULE)]
> - #[doc(hidden)]
> - #[no_mangle]
> - #[link_section = \".exit.text\"]
> - pub extern \"C\" fn cleanup_module() {{
> - // SAFETY:
> - // - This function is inaccessible to the outside
> due to the double
> - // module wrapping it. It is called exactly once
> by the C side via its
> - // unique name,
> - // - furthermore it is only called after
> `init_module` has returned `0`
> - // (which delegates to `__init`).
> - unsafe {{ __exit() }}
> - }}
> -
> - #[cfg(MODULE)]
> - #[doc(hidden)]
> - #[used(compiler)]
> - #[link_section = \".exit.data\"]
> - static __UNIQUE_ID___addressable_cleanup_module: extern
> \"C\" fn() = cleanup_module;
> -
> - // Built-in modules are initialized through an initcall
> pointer
> - // and the identifiers need to be unique.
> - #[cfg(not(MODULE))]
> - #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))]
> - #[doc(hidden)]
> - #[link_section = \"{initcall_section}\"]
> - #[used(compiler)]
> - pub static __{ident}_initcall: extern \"C\" fn() ->
> - ::kernel::ffi::c_int = __{ident}_init;
> -
> - #[cfg(not(MODULE))]
> - #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)]
> - ::core::arch::global_asm!(
> - r#\".section \"{initcall_section}\", \"a\"
> - __{ident}_initcall:
> - .long __{ident}_init - .
> - .previous
> - \"#
> - );
> -
> - #[cfg(not(MODULE))]
> - #[doc(hidden)]
> - #[no_mangle]
> - pub extern \"C\" fn __{ident}_init() ->
> ::kernel::ffi::c_int {{
> - // SAFETY: This function is inaccessible to the
> outside due to the double
> - // module wrapping it. It is called exactly once by
> the C side via its
> - // placement above in the initcall section.
> - unsafe {{ __init() }}
> - }}
> -
> - #[cfg(not(MODULE))]
> - #[doc(hidden)]
> - #[no_mangle]
> - pub extern \"C\" fn __{ident}_exit() {{
> - // SAFETY:
> - // - This function is inaccessible to the outside
> due to the double
> - // module wrapping it. It is called exactly once
> by the C side via its
> - // unique name,
> - // - furthermore it is only called after
> `__{ident}_init` has
> - // returned `0` (which delegates to `__init`).
> - unsafe {{ __exit() }}
> - }}
> -
> - /// # Safety
> - ///
> - /// This function must only be called once.
> - unsafe fn __init() -> ::kernel::ffi::c_int {{
> - let initer =
> - <{type_} as
> ::kernel::InPlaceModule>::init(&super::super::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
> - // called once and `__exit` cannot be called before
> or during `__init`.
> - match unsafe {{
> initer.__pinned_init(__MOD.as_mut_ptr()) }} {{
> - Ok(m) => 0,
> - Err(e) => e.to_errno(),
> - }}
> - }}
> -
> - /// # Safety
> - ///
> - /// This function must
> - /// - only be called once,
> - /// - be called after `__init` has been called and
> returned `0`.
> - unsafe fn __exit() {{
> - // 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 `__init` was already called.
> - unsafe {{
> - // Invokes `drop()` on `__MOD`, which should be
> used for cleanup.
> - __MOD.assume_init_drop();
> - }}
> - }}
> - {modinfo}
> - }}
> - }}
> - mod module_parameters {{
> - {params}
> - }}
> - ",
> - type_ = info.type_,
> - name = info.name.value(),
> - ident = ident,
> - modinfo = modinfo.buffer,
> - params = modinfo.param_buffer,
> - initcall_section = ".initcall6.init"
> - )
> - .parse()
> - .expect("Error parsing formatted string into token stream."))
> + let modinfo_ts = modinfo.ts;
> + let params_ts = modinfo.param_ts;
> +
> + let ident_init = format_ident!("__{ident}_init");
> + let ident_exit = format_ident!("__{ident}_exit");
> + let ident_initcall = format_ident!("__{ident}_initcall");
> + let initcall_section = ".initcall6.init";
> +
> + let global_asm = format!(
> + r#".section "{initcall_section}", "a"
> + __{ident}_initcall:
> + .long __{ident}_init - .
> + .previous
> + "#
> + );
> +
> + let name_cstr =
> + Literal::c_string(&CString::new(name.value()).expect("name contains
> NUL-terminator"));
> +
> + Ok(quote! {
> + /// The module name.
> + ///
> + /// 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_;
> +
> + impl ::kernel::ModuleMetadata for #type_ {
> + const NAME: &'static ::kernel::str::CStr = #name_cstr;
> + }
> +
> + // Double nested modules, since then nobody can access the public
> items inside.
> + mod __module_init {
> + mod __module_init {
> + use super::super::#type_;
> + use pin_init::PinInit;
> +
> + /// The "Rust loadable module" mark.
> + //
> + // This may be best done another way later on, e.g. as a new
> modinfo
> + // key or a new section. For the moment, keep it simple.
> + #[cfg(MODULE)]
> + #[doc(hidden)]
> + #[used(compiler)]
> + static __IS_RUST_MODULE: () = ();
> +
> + static mut __MOD: ::core::mem::MaybeUninit<#type_> =
> + ::core::mem::MaybeUninit::uninit();
> +
> + // Loadable modules need to export the
> `{init,cleanup}_module` identifiers.
> + /// # Safety
> + ///
> + /// This function must not be called after module
> initialization, because it may be
> + /// freed after that completes.
> + #[cfg(MODULE)]
> + #[doc(hidden)]
> + #[no_mangle]
> + #[link_section = ".init.text"]
> + pub unsafe extern "C" fn init_module() ->
> ::kernel::ffi::c_int {
> + // SAFETY: This function is inaccessible to the outside
> due to the double
> + // module wrapping it. It is called exactly once by the
> C side via its
> + // unique name.
> + unsafe { __init() }
> + }
> +
> + #[cfg(MODULE)]
> + #[doc(hidden)]
> + #[used(compiler)]
> + #[link_section = ".init.data"]
> + static __UNIQUE_ID___addressable_init_module: unsafe extern
> "C" fn() -> i32 =
> + init_module;
> +
> + #[cfg(MODULE)]
> + #[doc(hidden)]
> + #[no_mangle]
> + #[link_section = ".exit.text"]
> + pub extern "C" fn cleanup_module() {
> + // SAFETY:
> + // - This function is inaccessible to the outside due to
> the double
> + // module wrapping it. It is called exactly once by
> the C side via its
> + // unique name,
> + // - furthermore it is only called after `init_module`
> has returned `0`
> + // (which delegates to `__init`).
> + unsafe { __exit() }
> + }
> +
> + #[cfg(MODULE)]
> + #[doc(hidden)]
> + #[used(compiler)]
> + #[link_section = ".exit.data"]
> + static __UNIQUE_ID___addressable_cleanup_module: extern "C"
> fn() = cleanup_module;
> +
> + // Built-in modules are initialized through an initcall
> pointer
> + // and the identifiers need to be unique.
> + #[cfg(not(MODULE))]
> + #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))]
> + #[doc(hidden)]
> + #[link_section = #initcall_section]
> + #[used(compiler)]
> + pub static #ident_initcall: extern "C" fn() ->
> + ::kernel::ffi::c_int = #ident_initcall;
> +
> + #[cfg(not(MODULE))]
> + #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)]
> + ::core::arch::global_asm!(#global_asm);
> +
> + #[cfg(not(MODULE))]
> + #[doc(hidden)]
> + #[no_mangle]
> + pub extern "C" fn #ident_init() -> ::kernel::ffi::c_int {
> + // SAFETY: This function is inaccessible to the outside
> due to the double
> + // module wrapping it. It is called exactly once by the
> C side via its
> + // placement above in the initcall section.
> + unsafe { __init() }
> + }
> +
> + #[cfg(not(MODULE))]
> + #[doc(hidden)]
> + #[no_mangle]
> + pub extern "C" fn #ident_exit() {
> + // SAFETY:
> + // - This function is inaccessible to the outside due to
> the double
> + // module wrapping it. It is called exactly once by
> the C side via its
> + // unique name,
> + // - furthermore it is only called after `#ident_init`
> has
> + // returned `0` (which delegates to `__init`).
> + unsafe { __exit() }
> + }
> +
> + /// # Safety
> + ///
> + /// This function must only be called once.
> + unsafe fn __init() -> ::kernel::ffi::c_int {
> + let initer =
> + <#type_ as
> ::kernel::InPlaceModule>::init(&super::super::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
> + // called once and `__exit` cannot be called before or
> during `__init`.
> + match unsafe { initer.__pinned_init(__MOD.as_mut_ptr())
> } {
> + Ok(m) => 0,
> + Err(e) => e.to_errno(),
> + }
> + }
> +
> + /// # Safety
> + ///
> + /// This function must
> + /// - only be called once,
> + /// - be called after `__init` has been called and returned
> `0`.
> + unsafe fn __exit() {
> + // 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 `__init` was already called.
> + unsafe {
> + // Invokes `drop()` on `__MOD`, which should be used
> for cleanup.
> + __MOD.assume_init_drop();
> + }
> + }
> +
> + #modinfo_ts
> + }
> + }
> +
> + mod module_parameters {
> + #params_ts
> + }
> + })
> }
> --
> 2.51.2
>
>