Re: [PATCH v2] rust: add `module_params` macro

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 24.08.24 23:23, Trevor Gross wrote:
> On Sat, Aug 24, 2024 at 8:16 AM Benno Lossin <benno.lossin@xxxxxxxxx> wrote:
>>> We shouldn't enable `const_mut_refs`. It is indeed close to
>>> stabilization, but it is still kind of churny right now and we don't
>>> want to enable the sharp edges everywhere.
>>>
>>> If the change from `static mut` to `UnsafeCell` that I mentioned above
>>> happens, `addr_of_mut!` turns into a `.get().cast::<...>()` takes the
>>> place of `addr_of_mut!` and doesn't require this feature (and also
>>> isn't unsafe).
>>
>> I think this is a good idea. There might only be a problem with not
>> being `Sync` though... So probably need to use `SyncUnsafeCell` instead.
> 
> Ah whoops, yeah that is what I meant.
> 
>>> If you prefer not to make that change, I think
>>> `addr_of!(...).cast_mut()` might be the best solution.
>>
>> Won't that be resulting in the wrong provenance? I.e. the pointer won't
>> be allowed to write to that location?
>>
>> I just checked with miri, it doesn't complain (even with
>> `strict-provenance`), so I guess it's fine? It feels rather wrong to me
>> to allow writing through a pointer obtained via `addr_of!`.
> 
> I think that `static mut` gets the interior mutability rules that
> `UnsafeCell` has, that *const and *mut become interchangeable. Quick
> demo for the `UnsafeCell` at [1]. We would probably have to ask opsem
> to clarify.

I see. I suggested to Andreas to change it to the current version, seems
like I was wrong on this... I feel like the `SyncUnsafeCell` version is
still better, since we can avoid a `static mut`.

> Coincidentally I had been talking to Ralf about this very pattern
> before seeing this, at [2].
> 
>>> Other thought: would a wrapper type make more sense here? Something like this:
>>>
>>> ```
>>> /* implementation */
>>> struct ModParam<T>(UnsafeCell<T>);
>>> [...]
>>> module! {
>>>     // ...
>>>     // instantiated as:
>>>     // `static MY_PARAM: ModParam<i64> = ModParam::new(1);`
>>
>> We used to do this, but it lead to problems: normally the parameter has
>> a lower case name, since that's the convention in the kernel. [...]
> 
> To me it seemed logical to keep the uppercase names here since it
> matches the convention for what they are (statics), and the macro
> could lowercase it for the bits exposed to the kernel. But I
> absolutely get the consistency argument here.

That would also be an option, but it might be confusing for people, they
enter a SCREAMING_CASE_SNAKE name, but when they start the kernel it
doesn't work.

>> [...] But then
>> pattern matching prioritises the static instead of introducing it as a
>> local parameter:
>>
>>     let MY_PARAM = ...;
>>
>> would fail, since you can't match MY_PARAM.
>>
>> This is also the reason why they live in their own module.
> 
> I'm not sure I follow the example here. It looks like it is shadowing
> a static's name as a local, why would you want that? Or if you meant
> the other way around `let SomePat(...) = MY_PARAM`, wouldn't it just
> be `let SomePat(...) = MY_PARAM.get()`? (Sorry if I missed some
> context here).

Yeah I expressed this poorly, the problem before was that you would
write:

    module! {
        /* ... */
        params: {
            foo: i64 {
                default: 1,
                description: "foo",
            }
        }
    }

    pub struct MyDriver {
        foo: i64,
    }

    impl Module for MyDriver {
        fn init(_: &'static ThisModule) -> Result<Self> {
            let foo = foo.read();
            //  ^^^ cannot be named the same as a static
            Ok(Self { foo })
        }
    }

>> But you can still do the modification of creating `ModParam` and using
>> that as the type of the static.
> 
> Do you mean an expansion like this?
> 
>     // module_parameters is kind of a long name
>     mod mod_params {
>         #[allow(non_upper_case_globals)]
>        static my_param: ModParam<i32> = ModParam::new(...);
>     }

Yes that's what I meant, although `my_param` should be `pub(crate)`.

> I don't mind that, even if the name is a bit weird by rust conventions.

Yeah, but I think since they are in their own module, it's fine.

One thing that we need to decide would be if we want

    mod_params::my_param::read()

or

    mod_params::my_param.read()

I slightly prefer the first one, which would mean that the expansion
would have to be:

    mod mod_params {
        pub(crate) enum my_param {}
        static my_param_value: ModParam<i32> = ModParam::new(...);
        impl my_param {
            pub(crate) fn read() {
                my_param_value.read()
            }
            /* ... */
        }
    }

> (For what it's worth, I used this wrapper type pattern for a plugin
> project that does shared variables in a similar way. I have been quite
> happy with it.)

Good to know!

---
Cheers,
Benno

> 
> - Trevor
> 
> [1]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=43664620f50384b7a3d5bf74ce7c3e39
> [2]: https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/topic/More.20accidental.20stabilization






[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux