On Fri Jun 20, 2025 at 1:29 PM CEST, Andreas Hindborg wrote: > "Benno Lossin" <los...@kernel.org> writes: >> On Thu Jun 12, 2025 at 3:40 PM CEST, Andreas Hindborg wrote: >>> +/// A wrapper for kernel parameters. >>> +/// >>> +/// This type is instantiated by the [`module!`] macro when module >>> parameters are >>> +/// defined. You should never need to instantiate this type directly. >>> +/// >>> +/// Note: This type is `pub` because it is used by module crates to access >>> +/// parameter values. >>> +#[repr(transparent)] >>> +pub struct ModuleParamAccess<T> { >>> + data: core::cell::UnsafeCell<T>, >>> +} >>> + >>> +// SAFETY: We only create shared references to the contents of this >>> container, >>> +// so if `T` is `Sync`, so is `ModuleParamAccess`. >>> +unsafe impl<T: Sync> Sync for ModuleParamAccess<T> {} >>> + >>> +impl<T> ModuleParamAccess<T> { >>> + #[doc(hidden)] >>> + pub const fn new(value: T) -> Self { >>> + Self { >>> + data: core::cell::UnsafeCell::new(value), >>> + } >>> + } >>> + >>> + /// Get a shared reference to the parameter value. >>> + // Note: When sysfs access to parameters are enabled, we have to pass >>> in a >>> + // held lock guard here. >>> + pub fn get(&self) -> &T { >>> + // SAFETY: As we only support read only parameters with no sysfs >>> + // exposure, the kernel will not touch the parameter data after >>> module >>> + // initialization. >> >> This should be a type invariant. But I'm having difficulty defining one >> that's actually correct: after parsing the parameter, this is written >> to, but when is that actually? > > For built-in modules it is during kernel initialization. For loadable > modules, it during module load. No code from the module will execute > before parameters are set.
Gotcha and there never ever will be custom code that is executed before/during parameter setting (so code aside from code in `kernel`)? >> Would we eventually execute other Rust >> code during that time? (for example when we allow custom parameter >> parsing) > > I don't think we will need to synchronize because of custom parameter > parsing. Parameters are initialized sequentially. It is not a problem if > the custom parameter parsing code name other parameters, because they > are all initialized to valid values (as they are statics). If you have `&'static i64`, then the value at that reference is never allowed to change. >> This function also must never be `const` because of the following: >> >> module! { >> // ... >> params: { >> my_param: i64 { >> default: 0, >> description: "", >> }, >> }, >> } >> >> static BAD: &'static i64 = module_parameters::my_param.get(); >> >> AFAIK, this static will be executed before loading module parameters and >> thus it makes writing to the parameter UB. > > As I understand, the static will be initialized by a constant expression > evaluated at compile time. I am not sure what happens when this is > evaluated in const context: > > pub fn get(&self) -> &T { > // SAFETY: As we only support read only parameters with no sysfs > // exposure, the kernel will not touch the parameter data after module > // initialization. > unsafe { &*self.data.get() } > } > > Why would that not be OK? I would assume the compiler builds a dependency > graph > when initializing statics? Yes it builds a dependency graph, but that is irrelevant? The problem is that I can create a `'static` reference to the inner value *before* the parameter is written-to (as the static is initialized before the parameters). --- Cheers, Benno