Re: [PATCH v13 2/6] rust: introduce module_param module
From: Benno Lossin
Date: Mon Jun 30 2025 - 08:28:10 EST
On Mon Jun 30, 2025 at 1:18 PM CEST, Andreas Hindborg wrote:
> "Benno Lossin" <lossin@xxxxxxxxxx> writes:
>> On Fri Jun 27, 2025 at 9:57 AM CEST, Andreas Hindborg wrote:
>>> Andreas Hindborg <a.hindborg@xxxxxxxxxx> writes:
>>>> "Benno Lossin" <lossin@xxxxxxxxxx> writes:
>>>>> That's good to know, then let's try to go for something simple.
>>>>>
>>>>> I don't think that we can just use a `Mutex<T>`, because we don't have a
>>>>> way to create it at const time... I guess we could have
>>>>>
>>>>> impl<T> Mutex<T>
>>>>> /// # Safety
>>>>> ///
>>>>> /// The returned value needs to be pinned and then `init` needs
>>>>> /// to be called before any other methods are called on this.
>>>>> pub unsafe const fn const_new() -> Self;
>>>>>
>>>>> pub unsafe fn init(&self);
>>>>> }
>>>>>
>>>>> But that seems like a bad idea, because where would we call the `init`
>>>>> function? That also needs to be synchronized...
>>>>
>>>> Ah, that is unfortunate. The init function will not run before this, so
>>>> we would need a `Once` or an atomic anyway to initialize the lock.
>>>>
>>>> I am not sure if we are allowed to sleep during this, I would have to
>>>> check. But then we could use a spin lock.
>>>>
>>>> We will need the locking anyway, when we want to enable sysfs write
>>>> access to the parameters.
>>>>
>>>>>
>>>>> Maybe we can just like you said use an atomic bool?
>>>>
>>>> Sigh, I will have to check how far that series has come.
>>>>
>>>
>>> I think I am going to build some kind of `Once` feature on top of
>>> Boqun's atomic series [1], so that we can initialize a lock in these
>>> statics. We can't use `global_lock!`, because that depends on module
>>> init to initialize the lock before first use.
>>
>> Sounds good, though we probably don't want to name it `Once`. Since it
>> is something that will be populated in the future, but not by some
>> random accessor, but rather a specific populator.
>>
>> So maybe:
>>
>> pub struct Delayed<T> {
>> dummy: T,
>> real: Opaque<T>,
>> populated: Atomic<bool>, // or Atomic<Flag>
>> writing: Atomic<bool>, // or Atomic<Flag>
>> }
>>
>> impl<T> Delayed<T> {
>> pub fn new(dummy: T) -> Self {
>> Self {
>> dummy,
>> real: Opaque::uninit(),
>> populated: Atomic::new(false),
>> writing: Atomic::new(false),
>> }
>> }
>>
>> pub fn get(&self) -> &T {
>> if self.populated.load(Acquire) {
>> unsafe { &*self.real.get() }
>> } else {
>> // maybe print a warning here?
>> // or maybe let the user configure this in `new()`?
>> &self.dummy
>> }
>> }
>>
>> pub fn populate(&self, value: T) {
>> if self.writing.cmpxchg(false, true, Release) {
>> unsafe { *self.real.get() = value };
>> self.populated.store(true, Release);
>> } else {
>> pr_warn!("`Delayed<{}>` written to twice!\n", core::any::type_name::<T>());
>> }
>> }
>> }
>>
>> (no idea if the orderings are correct, I always have to think way to
>> much about that... especially since our atomics seem to only take one
>> ordering in compare_exchange?)
>>
>>> As far as I can tell, atomics may not land in v6.17, so this series
>>> will probably not be ready for merge until v6.18 at the earliest.
>>
>> Yeah, sorry about that :(
>
> Actually, perhaps we could aim at merging this code without this
> synchronization?
I won't remember this issue in a few weeks and I fear that it will just
get buried. In fact, I already had to re-read now what the actual issue
was...
> The lack of synchronization is only a problem if we
> support custom parsing. This patch set does not allow custom parsing
> code, so it does not suffer this issue.
... In doing that, I saw my original example of UB:
module! {
// ...
params: {
my_param: i64 {
default: 0,
description: "",
},
},
}
static BAD: &'static i64 = module_parameters::my_param.get();
That can happen without custom parsing, so it's still a problem...
---
Cheers,
Benno