Re: [PATCH v7] asus-wmi: Add support for custom fan curves

From: Barnabás Pőcze
Date: Wed Sep 01 2021 - 11:24:47 EST


Hi


> [...]
> >> --- a/drivers/platform/x86/asus-wmi.c
> >> +++ b/drivers/platform/x86/asus-wmi.c
> >> [...]
> >> +/*
> >> + * Returns as an error if the method output is not a buffer.
> >> Typically this
> >
> > It seems to me it will simply leave the output buffer uninitialized
> > if something
> > other than ACPI_TYPE_BUFFER and ACPI_TYPE_INTEGER is encountered and
> > return 0.
>
> Oops, see below inline reply:
>
> >
> >
> >> + * means that the method called is unsupported.
> >> + */
> >> +static int asus_wmi_evaluate_method_buf(u32 method_id,
> >> + u32 arg0, u32 arg1, u8 *ret_buffer)
> >> +{
> >> + struct bios_args args = {
> >> + .arg0 = arg0,
> >> + .arg1 = arg1,
> >> + .arg2 = 0,
> >> + };
> >> + struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
> >> + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> >> + acpi_status status;
> >> + union acpi_object *obj;
> >> + u32 int_tmp = 0;
> >> +
> >> + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
> >> + &input, &output);
> >> +
> >> + if (ACPI_FAILURE(status))
> >> + return -EIO;
> >> +
> >> + obj = (union acpi_object *)output.pointer;
> >> +
> >> + if (obj && obj->type == ACPI_TYPE_INTEGER) {
> >> + int_tmp = (u32) obj->integer.value;
> >> + if (int_tmp == ASUS_WMI_UNSUPPORTED_METHOD)
> >> + return -ENODEV;
> >> + return int_tmp;
> >
> > Is anything known about the possible values? You are later
> > using it as if it was an errno (e.g. in `custom_fan_check_present()`).
> >
> > And `obj` is leaked in both of the previous two returns.
>
> The return for the method we're calling in this patch returns 0 if the
> input arg has no match.
>
> >
> >
> >> + }
> >> +
> >> + if (obj && obj->type == ACPI_TYPE_BUFFER)
> >> + memcpy(ret_buffer, obj->buffer.pointer, obj->buffer.length);
> >
> > I would suggest you add a "size_t size" argument to this function, and
> > return -ENOSPC/-ENODATA depending on whether the returned buffer is
> > too
> > big/small. Maybe return -ENODATA if `obj` is NULL, too.
>
> Got it. So something like this would be suitable?
>
> if (obj && obj->type == ACPI_TYPE_BUFFER)
> if (obj->buffer.length < size)
> err = -ENOSPC;
> if (!obj->buffer.length)
> err = -ENODATA;
> if (err) {
> kfree(obj);
> return err;
> }
> memcpy(ret_buffer, obj->buffer.pointer, obj->buffer.length);
> }
>
> if (obj && obj->type == ACPI_TYPE_INTEGER)
> int_tmp = (u32) obj->integer.value;
>
> kfree(obj);
>
> if (int_tmp == ASUS_WMI_UNSUPPORTED_METHOD)
> return -ENODEV;
>
> /* There is at least one method that returns a 0 with no buffer */
> if (obj == NULL || int_tmp == 0)
> return -ENODATA;
>
> return 0;
>

I had something like the following in mind:

int err = 0;
/* ... */
obj = output.pointer;
if (!obj)
return -ENODATA;

switch (obj->type) {
case ACPI_TYPE_BUFFER:
if (obj->buffer.length < size)
err = -ENODATA;
else if (obj->buffer.length > size)
err = -ENOSPC;
else
memcpy(ret_buffer, obj->buffer.pointer, size);
break;
case ACPI_TYPE_INTEGER:
switch (obj->integer.value) {
case ASUS_WMI_UNSUPPORTED_METHOD:
err = -EOPNOTSUPP;
break;
default:
err = -ENODATA;
break;
}
break;
default:
err = -ENODATA;
break;
}

kfree(obj);

return err;


> >
> >
> >> +
> >> + kfree(obj);
> >> +
> >> + return 0;
> >> +}
> [...]
> >> +/*
> >> + * Called only by throttle_thermal_policy_write()
> >> + */
> >
> > Am I correct in thinking that the firmware does not actually
> > support specifying fan curves for each mode, only a single one,
> > and the fan curve switching is done by this driver when
> > the performance mode is changed?
>
> I'm not 100% certain on this. The WMI method 0x00110024 takes an arg
> 0,1,2 which then returns some factory stored fan profiles, these fit
> the profiles of ASUS_THROTTLE_THERMAL_POLICY_*, but with 1 and 2
> swapped.
>
> Looking at the SET part, it seems to write to a different location than
> where the GET is fetching information.
>

The, unfortunately, that is not as simple as I initially thought...


> Because of the fact there are three sets of curves to get, I thought it
> would be good for users to be able to set per profile. I don't think
> the set is retained in acpi if the profile is switched.
>
> Do you think it would be best to not have the ability to store per
> profile in kernel?

If there was a method to set a fan curve, and one to retrieve it,
I would suggest just exposing that via the pwmN_auto_pointM_{pwm,temp}
attributes on a hwmon device, and that the profile-dependent switching
be implemented somewhere else. As far as I see, there is already
existing infrastructure for integrating such a feature [0]
(but please correct me if I'm wrong).

This would simplify the kernel code, add no new ABI, and
potentially provide greater control over policy for the
user space.


> How would I choose which profile get to populate the
> initial data with if so?

I assume there isn't a method that can query
the current fan curve (or it is unknown)?


> [...]

[0]: https://gitlab.com/asus-linux/asusctl


Best regards,
Barnabás Pőcze