From: Armin Wolf <W_Armin@gmx.de>
To: Alexis Belmonte <alexbelm48@gmail.com>,
ilpo.jarvinen@linux.intel.com, hdegoede@redhat.com
Cc: platform-driver-x86@vger.kernel.org
Subject: Re: [PATCHv7] platform/x86: hp-wmi: Fix platform profile option switch bug on Omen and Victus laptops
Date: Mon, 1 Jul 2024 18:00:58 +0200 [thread overview]
Message-ID: <7b2d2f5d-a1b8-403b-a9eb-b05fa074cfee@gmx.de> (raw)
In-Reply-To: <7f058c69-8a3c-479d-bebf-9878a5d64f06@gmx.de>
Am 01.07.24 um 17:56 schrieb Armin Wolf:
> Am 01.07.24 um 17:00 schrieb Alexis Belmonte:
>
>> Fix a platform profile option switch/getter bug on some Omen and Victus
>> laptops dismissing userspace choice when selecting performance mode in
>> inadequate conditions (e.g. by being disconnected from the AC power
>> plug)
>> by
>>
>> - hooking an ACPI notify handler through the
>> omen_register_powersource_notifier_handler method that listens
>> to AC
>> power source changes (plugging in/out the AC power plug)
>>
>> - keeping an intermediate active_platform_profile variable that is
>> set when userspace changes the platform profile setting
>>
>> - restoring the selected platform profile kept in
>> active_platform_profile when AC power is plugged back into the
>> laptop, unless if the user decided to alter the platform profile
>> mid-way
>>
>> This ensures that the driver follows the principles defined in the
>> Platform Profile Selection page of the Kernel documentation on those
>> kind
>> of laptops; which is to not "(...) let userspace know about any
>> sub-optimal conditions which are impeding reaching the requested
>> performance level".
>>
>> Since the Omen and Victus laptops share the same embedded controller
>> system, the fix is applicable to both categories of laptops.
>>
>> This patch also provides improvements to how the driver sets/gets the
>> platform profile through the embedded controller, by introducing
>> intermediary functions to leverage code from
>> platform_profile_omen_set and
>> callers.
>>
>> Signed-off-by: Alexis Belmonte <alexbelm48@gmail.com>
>> ---
>> V1 -> V2: - Use register_acpi_notifier and unregister_acpi_notifier
>> instead of
>> hooking straight through ACPI node \\_SB.ADP1
>> V2 -> V3: - Rely on power_supply_is_system_supplied() instead of an
>> EC-specific
>> field to determine if the laptop is plugged in
>> - Refactor omen_powersource_notify_handler to
>> omen_powersource_event
>> - Refactor omen_powersource_register_notifier_handler to
>> omen_register_powersource_event_handler
>> - Use a mutex to protect the active_platform_profile
>> variable from
>> being altered while the handler is executed
>> V3 -> V4: - Remove the unnecessary enum declaration remains from the
>> initial
>> implementation
>> V4 -> V5: - Drop unnecessary modifications from the patch
>> - Call platform_profile_omen_get in
>> platform_profile_victus_get to
>> avoid code duplication
>> - Give-up module initialization if we fail to register the
>> ACPI
>> notifier handler
>> - Fix code style issues reported by checkpatch.pl --strict
>> - Add intermediary/helper platform_profile_omen_set_ec and
>> platform_profile_victus_set_ec functions to leverage
>> code from
>> platform_profile_omen_set and callers, thus simplifying
>> omen_powersource_event
>> - Fix dead-lock when restoring active_platform_profile
>> when the AC
>> power is plugged back into the laptop
>> V5 -> V6: - Drop unnecessary modifications from the patch
>> V6 -> V7: - Drop EC platform profile readback after set
>> - Lock the active_platform_profile mutex unconditionally
>> - Drop the usage of ACPI_FAILURE in favor of a simpler
>> error check
>> when registering/unregistering the ACPI notifier
>> - Initialize active_platform_profile in thermal_profile_setup
>> ---
>> drivers/platform/x86/hp/hp-wmi.c | 211 +++++++++++++++++++++++++++++--
>> 1 file changed, 200 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/platform/x86/hp/hp-wmi.c
>> b/drivers/platform/x86/hp/hp-wmi.c
>> index 5fa553023842..6ffc9d7ad8c1 100644
>> --- a/drivers/platform/x86/hp/hp-wmi.c
>> +++ b/drivers/platform/x86/hp/hp-wmi.c
>> @@ -24,6 +24,7 @@
>> #include <linux/platform_profile.h>
>> #include <linux/hwmon.h>
>> #include <linux/acpi.h>
>> +#include <linux/power_supply.h>
I nearly forgot: Please add "depends on POWER_SUPPLY" to the kconfig entry of HP_WMI
in drivers/platform/x86/hp/Kconfig.
Thanks,
Armin Wolf
>> #include <linux/rfkill.h>
>> #include <linux/string.h>
>> #include <linux/dmi.h>
>> @@ -42,6 +43,8 @@
>> MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4");
>> #define HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET 0x63
>> #define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95
>>
>> +#define ACPI_AC_CLASS "ac_adapter"
>> +
>> #define zero_if_sup(tmp) (zero_insize_support?0:sizeof(tmp)) // use
>> when zero insize is required
>>
>> /* DMI board names of devices that should use the omen specific
>> path for
>> @@ -259,10 +262,18 @@ static const struct key_entry hp_wmi_keymap[] = {
>> { KE_END, 0 }
>> };
>>
>> +/*
>> + * Mutex for the active_platform_profile variable,
>> + * see omen_powersource_event.
>> + */
>> +DEFINE_MUTEX(active_platform_profile_lock);
>> +
>> static struct input_dev *hp_wmi_input_dev;
>> static struct input_dev *camera_shutter_input_dev;
>> static struct platform_device *hp_wmi_platform_dev;
>> static struct platform_profile_handler platform_profile_handler;
>> +static struct notifier_block platform_power_source_nb;
>> +static enum platform_profile_option active_platform_profile;
>> static bool platform_profile_support;
>> static bool zero_insize_support;
>>
>> @@ -1194,8 +1205,7 @@ static int __init hp_wmi_rfkill2_setup(struct
>> platform_device *device)
>> return err;
>> }
>>
>> -static int platform_profile_omen_get(struct platform_profile_handler
>> *pprof,
>> - enum platform_profile_option *profile)
>> +static int platform_profile_omen_get_ec(enum platform_profile_option
>> *profile)
>> {
>> int tp;
>>
>> @@ -1223,6 +1233,30 @@ static int platform_profile_omen_get(struct
>> platform_profile_handler *pprof,
>> return 0;
>> }
>>
>> +static int platform_profile_omen_get(struct platform_profile_handler
>> *pprof,
>> + enum platform_profile_option *profile)
>> +{
>> + enum platform_profile_option selected_platform_profile;
>> +
>> + /*
>> + * We directly return the stored platform profile, as the embedded
>> + * controller will not accept switching to the performance
>> option when
>> + * the conditions are not met (e.g. the laptop is not plugged in).
>> + *
>> + * If we directly return what the EC reports, the platform
>> profile will
>> + * immediately "switch back" to normal mode, which is against the
>> + * expected behaviour from a userspace point of view, as
>> described in
>> + * the Platform Profile Section page of the kernel documentation.
>> + *
>> + * See also omen_powersource_event.
>> + */
>> + mutex_lock(&active_platform_profile_lock);
>> + selected_platform_profile = active_platform_profile;
>> + mutex_unlock(&active_platform_profile_lock);
>> +
>> + return selected_platform_profile;
>> +}
>> +
>> static bool has_omen_thermal_profile_ec_timer(void)
>> {
>> const char *board_name = dmi_get_system_info(DMI_BOARD_NAME);
>> @@ -1245,8 +1279,7 @@ inline int omen_thermal_profile_ec_timer_set(u8
>> value)
>> return ec_write(HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET, value);
>> }
>>
>> -static int platform_profile_omen_set(struct platform_profile_handler
>> *pprof,
>> - enum platform_profile_option profile)
>> +static int platform_profile_omen_set_ec(enum platform_profile_option
>> *profile)
>
> Hi,
>
> there is no reason anymore for profile to be a pointer, please fix this.
>
>> {
>> int err, tp, tp_version;
>> enum hp_thermal_profile_omen_flags flags = 0;
>> @@ -1256,7 +1289,7 @@ static int platform_profile_omen_set(struct
>> platform_profile_handler *pprof,
>> if (tp_version < 0 || tp_version > 1)
>> return -EOPNOTSUPP;
>>
>> - switch (profile) {
>> + switch (*profile) {
>> case PLATFORM_PROFILE_PERFORMANCE:
>> if (tp_version == 0)
>> tp = HP_OMEN_V0_THERMAL_PROFILE_PERFORMANCE;
>> @@ -1288,7 +1321,7 @@ static int platform_profile_omen_set(struct
>> platform_profile_handler *pprof,
>> if (err < 0)
>> return err;
>>
>> - if (profile == PLATFORM_PROFILE_PERFORMANCE)
>> + if (*profile == PLATFORM_PROFILE_PERFORMANCE)
>> flags = HP_OMEN_EC_FLAGS_NOTIMER |
>> HP_OMEN_EC_FLAGS_TURBO;
>>
>> @@ -1300,6 +1333,25 @@ static int platform_profile_omen_set(struct
>> platform_profile_handler *pprof,
>> return 0;
>> }
>>
>> +static int platform_profile_omen_set(struct platform_profile_handler
>> *pprof,
>> + enum platform_profile_option profile)
>> +{
>> + int err;
>> +
>> + mutex_lock(&active_platform_profile_lock);
>> +
>> + err = platform_profile_omen_set_ec(&profile);
>> + if (err < 0) {
>> + mutex_unlock(&active_platform_profile_lock);
>> + return err;
>> + }
>> +
>> + active_platform_profile = profile;
>> + mutex_unlock(&active_platform_profile_lock);
>> +
>> + return 0;
>> +}
>> +
>> static int thermal_profile_get(void)
>> {
>> return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY);
>> @@ -1381,8 +1433,7 @@ static bool is_victus_thermal_profile(void)
>> board_name) >= 0;
>> }
>>
>> -static int platform_profile_victus_get(struct
>> platform_profile_handler *pprof,
>> - enum platform_profile_option *profile)
>> +static int platform_profile_victus_get_ec(enum
>> platform_profile_option *profile)
>> {
>> int tp;
>>
>> @@ -1407,12 +1458,18 @@ static int platform_profile_victus_get(struct
>> platform_profile_handler *pprof,
>> return 0;
>> }
>>
>> -static int platform_profile_victus_set(struct
>> platform_profile_handler *pprof,
>> - enum platform_profile_option profile)
>> +static int platform_profile_victus_get(struct
>> platform_profile_handler *pprof,
>> + enum platform_profile_option *profile)
>> +{
>> + /* Same behaviour as platform_profile_omen_get */
>> + return platform_profile_omen_get(pprof, profile);
>> +}
>> +
>> +static int platform_profile_victus_set_ec(enum
>> platform_profile_option *profile)
>> {
>> int err, tp;
>>
>> - switch (profile) {
>> + switch (*profile) {
>
> Same as above.
>
>> case PLATFORM_PROFILE_PERFORMANCE:
>> tp = HP_VICTUS_THERMAL_PROFILE_PERFORMANCE;
>> break;
>> @@ -1433,10 +1490,124 @@ static int
>> platform_profile_victus_set(struct platform_profile_handler *pprof,
>> return 0;
>> }
>>
>> +static int platform_profile_victus_set(struct
>> platform_profile_handler *pprof,
>> + enum platform_profile_option profile)
>> +{
>> + int err;
>> +
>> + mutex_lock(&active_platform_profile_lock);
>> +
>> + err = platform_profile_victus_set_ec(&profile);
>> + if (err < 0) {
>> + mutex_unlock(&active_platform_profile_lock);
>> + return err;
>> + }
>> +
>> + active_platform_profile = profile;
>> + mutex_unlock(&active_platform_profile_lock);
>> +
>> + return 0;
>> +}
>> +
>> +static int omen_powersource_event(struct notifier_block *nb,
>> + unsigned long value,
>> + void *data)
>> +{
>> + struct acpi_bus_event *event_entry = data;
>> + enum platform_profile_option actual_profile;
>> + int err;
>> +
>> + if (strcmp(event_entry->device_class, ACPI_AC_CLASS) != 0)
>> + return NOTIFY_DONE;
>> +
>> + pr_debug("Received power source device event\n");
>> +
>> + mutex_lock(&active_platform_profile_lock);
>> +
>> + if (is_omen_thermal_profile()) {
>> + err = platform_profile_omen_get_ec(&actual_profile);
>> + } else if (is_victus_thermal_profile()) {
>> + err = platform_profile_victus_get_ec(&actual_profile);
>> + }
>
> If neither is_omen_thermal_profile() nor is_victus_thermal_profile()
> is true,
> then err is uninitialized here.
>
> Please return without an error if both conditions are not true.
>
>> +
>> + if (err < 0) {
>> + pr_warn("Failed to read current platform profile (%d)\n", err);
>> +
>> + /*
>> + * Although we failed to get the current platform profile, we
>> + * still want the other event consumers to process it.
>> + */
>> + return NOTIFY_DONE;
>> + }
>> +
>> + /*
>> + * If we're back on AC and that the user-chosen power profile is
>> + * different from what the EC reports, we restore the user-chosen
>> + * one.
>> + */
>> + if (power_supply_is_system_supplied() >= 0 ||
>> + active_platform_profile != actual_profile) {
>> + mutex_unlock(&active_platform_profile_lock);
>> +
>> + pr_debug("EC reports same platform profile, no platform
>> profile update required\n");
>> + return NOTIFY_DONE;
>> + }
>> +
>> + if (is_omen_thermal_profile()) {
>> + err = platform_profile_omen_set_ec(&active_platform_profile);
>> + } else if (is_victus_thermal_profile()) {
>> + err = platform_profile_victus_set_ec(&active_platform_profile);
>> + }
>
> Same problem as above, please set err to zero if both conditions are
> not true.
>
>> +
>> + if (err < 0) {
>> + mutex_unlock(&active_platform_profile_lock);
>> +
>> + pr_warn("Failed to restore platform profile (%d)\n", err);
>> + return NOTIFY_DONE;
>> + }
>> +
>> + mutex_unlock(&active_platform_profile_lock);
>> +
>> + return NOTIFY_OK;
>> +}
>> +
>> +static int omen_register_powersource_event_handler(void)
>> +{
>> + int err;
>> +
>> + if (is_omen_thermal_profile())
>> + err = platform_profile_omen_get_ec(&active_platform_profile);
>> + else if (is_victus_thermal_profile())
>> + err = platform_profile_victus_get_ec(&active_platform_profile);
>
> I will say it again: active_platform_profile has to be initialized
> _before_ thermal_profile_setup()
> calls platform_profile_register().
>
> This is the wrong place to initialize active_platform_profile, its too
> late. Please drop this.
>
>> +
>> + platform_power_source_nb.notifier_call = omen_powersource_event;
>> + err = register_acpi_notifier(&platform_power_source_nb);
>> +
>> + if (err < 0) {
>> + pr_warn("Failed to install ACPI power source notify
>> handler\n");
>> + return err;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void omen_unregister_powersource_event_handler(void)
>> +{
>> + int err;
>> +
>> + err = unregister_acpi_notifier(&platform_power_source_nb);
>> +
>> + if (err < 0)
>> + pr_err("Failed to remove ACPI power source notify handler\n");
>> +}
>> +
>> static int thermal_profile_setup(void)
>> {
>> int err, tp;
>>
>> + if (is_omen_thermal_profile() || is_victus_thermal_profile())
>> + active_platform_profile = PLATFORM_PROFILE_BALANCED;
>
> With "initializing active_platform_profile", i meant to actually read
> the current value from the EC.
>
> I think you can replace the calls of omen_thermal_profile_get() inside
> thermal_profile_setup()
> with platform_profile_omen_get_ec()/platform_profile_victus_get_ec()
> and use them to initialize
> active_platform_profile.
>
>> +
>> if (is_omen_thermal_profile()) {
>> tp = omen_thermal_profile_get();
>> if (tp < 0)
>> @@ -1534,6 +1705,15 @@ static int __init hp_wmi_bios_setup(struct
>> platform_device *device)
>>
>> thermal_profile_setup();
>>
>> + /*
>> + * Query the platform profile once to know which last power profile
>> + * was set.
>> + */
>> + err =
>> platform_profile_handler.profile_get(&platform_profile_handler,
>> + &active_platform_profile);
>
> Again, this is too late, the platform profile is already registered.
> Please drop.
>
>> + if (err < 0)
>> + return err;
>> +
>> return 0;
>> }
>>
>> @@ -1758,6 +1938,12 @@ static int __init hp_wmi_init(void)
>> goto err_unregister_device;
>> }
>>
>> + if (is_omen_thermal_profile() || is_victus_thermal_profile()) {
>> + err = omen_register_powersource_event_handler();
>> + if (err)
>> + goto err_unregister_device;
>> + }
>> +
>> return 0;
>>
>> err_unregister_device:
>> @@ -1772,6 +1958,9 @@ module_init(hp_wmi_init);
>>
>> static void __exit hp_wmi_exit(void)
>> {
>> + if (is_omen_thermal_profile() || is_victus_thermal_profile())
>> + omen_unregister_powersource_event_handler();
>> +
>> if (wmi_has_guid(HPWMI_EVENT_GUID))
>> hp_wmi_input_destroy();
>>
>
prev parent reply other threads:[~2024-07-01 16:01 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-01 15:00 [PATCHv7] platform/x86: hp-wmi: Fix platform profile option switch bug on Omen and Victus laptops Alexis Belmonte
2024-07-01 15:56 ` Armin Wolf
2024-07-01 16:00 ` Armin Wolf [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=7b2d2f5d-a1b8-403b-a9eb-b05fa074cfee@gmx.de \
--to=w_armin@gmx.de \
--cc=alexbelm48@gmail.com \
--cc=hdegoede@redhat.com \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=platform-driver-x86@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox