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

Benno Lossin
2024-08-06T08:09:23Z

On 05.08.24 12:55, Andreas Hindborg wrote:
> "Benno Lossin" <benno.lossin@proton.me> writes:
>> On 02.08.24 12:27, Andreas Hindborg wrote:
>>> At a higher level where the bindings supply the parsing functions, we
>>> can decide that passing an argument without a value yields a default
>>> parameter value. C does this for the predefined `bool` type. The
>>> predefined integer types does not support omitting the value.
>>>
>>> This patch only supports the higher level predefined parameter types,
>>> and does not allow modules to supply their own parameter parsing
>>> functions. None of the types we implement in this patch support passing
>>> the argument without a value. This is intentional to mirror the C
>>> implementation.
>>>
>>> To that end, I removed `NOARG_ALLOWED`, and changed the parsing function
>>> trait to:
>>>
>>> fn try_from_param_arg(arg: &'static [u8]) -> Result<Self>;
>>>
>>> If/when we start supporting types like `bool` or custom parsing
>>> functions provided by the module, we will have to update the signature
>>> to take an `Option` to represent the case where the user passed an
>>> argument without a value. However, to mimic C, the function must always
>>> return a value if successful, even if the user did not supply a value to
>>> the argument.
>>>
>>> Two different default values are in flight here. 1) the value that the
>>> parameter will have before the kernel calls `try_from_param_arg` via
>>> `set_param` and 2) the value to return from `try_from_param_arg` if the
>>> user did not pass a value with the argument.
>>>
>>> For a `bool` 1) would usually be `false` and 2) would always be `true`.
>>>
>>> For predefined types the module would not customize 2), but 1) is useful
>>> to customize. For custom types where the module supplies the parsing
>>> function, 2) would be implicitly given by the module in the parsing
>>> function.
>>>
>>> In this patch set, we only have 1 default value, namely 1). We do not
>>> need 2) because we do not support parameters without values.
>>
>> I am not sure that putting the default value of `my_module.param` into
>> the `ModuleParam` trait is a good idea. It feels more correct to me to
>> add an optional field to the part in `module!` that can be set to denote
>> this default value -- we might also want to change the name of
>> `default`, what do you think of `default_inactive` and `default_active`?
>
> For all the predefined parameter types, the module code would never set
> the `default_active` value. It should be part of the data parsing
> specification for the predefined argument types.

So if your module has an i32 parameter, you can't set a default value to
eg 1000?

> For custom parsing functions implemented in the module, it makes sense
> specifying this value in the trait.

Couldn't I just emulate the behavior from above by creating a
`struct MyI32(i32)` and having a default value?
In that case, why make it more difficult instead of providing a simple
way to do it?

>> Since one might want an option by default be `true` and when one writes
>> `my_module.param`, it should be `false`.
>
> I think this would be confusing, since the default C parameter types do
> not have this semantic. Specifying a default true boolean as an argument
> does not make it false in C land. TBH this also feels like the most sane
> thing to do.

Can't you also do the custom parameter parsing from above? ie have an
integer parameter with a default value?
Or is that frowned upon (ie not allowed through review)?

>> Also as the C side shows, having default values for integer types is not
>> really a good idea, since you might want a non-zero default value.
>> If one does not specify the `default_active` value, then the
>> `KERNEL_PARAM_OPS_FL_NOARG` is not set.
>
> I would rather predefine `KERNEL_PARAM_OPS_FL_NOARG` in the trait
> implementation like C does. We would set the flag for `bool` but not for
> integer types. For custom implementations of the trait, the developer
> can do whatever they want.

So we are only going to use it for `bool`?

>> If you don't want to implement this (which I can fully understand, since
>> we might get `syn` before anyone needs params with default values), then
>> we should write this idea down (maybe in an issue?). But regardless, I
>> would like to know your opinion on this topic.
>
> We can create he issue if this series is accepted without the feature.
>
>>
>>>>> I think we need to drop the default value if we adopt the arg without
>>>>> value scheme.
>>>>
>>>> Yes definitely. I don't see anything in the code doing this currently,
>>>> right?
>>>
>>> The current patch uses the default value given in the `module!` macro to
>>> initialize the parameter value.
>>
>> But what drops the default value, when an actual value is specified via
>> `my_module.param=value`? (or is the default value only assigned when
>> nothing is specified?)
>
> Some more confusion maybe. When I wrote "drop the default value", I
> meant remove it from the code, not specify it. I take it back though. We
> would use the value given in the `module!` macro `default` field as
> `default_inactive`. `default_active` is not required for integer types,
> since they always require a value for the argument.
>
> But the drop would happen in `set_param` when the return value of
> `core::ptr::replace` is dropped.

Ah I see, I missed that.

>>>>> We could also only allow `Copy` values to be used as Parameters (but
>>>> then `str` cannot be used as a parameter...), since they can't implement
>>>> `Drop`.
>>>
>>> We should plan on eventually supporting `String`.
>>
>> Yes.
>>
>>>>>>> Or we could just not adopt this feature in the Rust abstractions.
>>>>>>
>>>>>> Depends on how common this is and if people need to use it. I think that
>>>>>> what I proposed above isn't that complex, so it should be easy to
>>>>>> implement.
>>>>>
>>>>> Rust modules would just force people to add "my_module.param=1" instead
>>>>> of just "my_module.param". I think that is reasonable.
>>>>
>>>> Eh, why do we want to give that up? I don't think it's difficult to do.
>>>
>>> I just don't see the point. Just have the user pass `my_module.param=1`
>>> instead of omitting the value. Having multiple ways of specifying for
>>> instance a true boolean just leads to confusion. Some boolean parameters
>>> have a default value of `true`, for instance `nvme.use_cmb_sqes`. In
>>> this case specifying `nvme.use_cmb_sqes` has no effect, even though one
>>> might think it has.
>>
>> This just shows to me that a "global" default in `ModuleParam` is wrong,
>> since for `use_cmb_sqes` one could either have a negated flag, or no
>> default value, forcing the user to write `nvme.use_cmb_sqes=false`.
>
> I do not think it is a good idea to be able to override the value
> assigned to a parameter when it is given without a value. The more
> predictable a user interface is, the better.

By that argument, we should also not permit custom implementations to
specify a default value.

---
Cheers,
Benno