"Benno Lossin" <lossin@xxxxxxxxxx> writes: > On Fri Jun 20, 2025 at 1:29 PM CEST, Andreas Hindborg wrote: >> "Benno Lossin" <lossin@xxxxxxxxxx> 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). I see, I did not consider this situation. Thanks for pointing this out. Could we get around this without a lock maybe? If we change `ModuleParamAccess::get` to take a closure instead: /// Call `func` with a reference to the parameter value stored in `Self`. pub fn read(&self, func: impl FnOnce(&T)) { // SAFETY: As we only support read only parameters with no sysfs // exposure, the kernel will not touch the parameter data after module // initialization. let data = unsafe { &*self.data.get() }; func(data) } I think this would bound the lifetime of the reference passed to the closure to the duration of the call, right? Best regards, Andreas Hindborg