Re: [PATCH v6 11/13] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other
From: Rong Zhang
Date: Wed Apr 01 2026 - 14:42:53 EST
Hi Derek,
On Tue, 2026-03-31 at 18:12 +0000, Derek J. Clark wrote:
> From: Rong Zhang <i@xxxxxxxx>
>
> Currently, lenovo-wmi-gamezone depends on lenovo-wmi-other as the former
> imports symbols from the latter. The imported symbols are just used to
> register a notifier block. However, there is no runtime dependency
> between both drivers, and either of them can run without the other,
> which is the major purpose of using the notifier framework.
>
> Such a link-time dependency is non-optimal. A previous attempt to "fix"
> it made LENOVO_WMI_GAMEZONE select LENOVO_WMI_TUNING, which was
> fundamentally broken and resulted in undefined Kconfig behavior, as
> `select' cannot be used on a symbol with potentially unmet dependencies.
>
> Decouple both drivers by moving the thermal mode notifier chain to
> lenovo-wmi-helpers. Methods for notifier block (un)registration are
> exported for lenovo-wmi-gamezone, while a method for querying the
> current thermal mode are exported for lenovo-wmi-other.
>
> This turns the dependency graph from
>
> +------------ lenovo-wmi-gamezone
> | |
> v |
> lenovo-wmi-helpers |
> ^ |
> | V
> +------------ lenovo-wmi-other
>
> into
>
> +------------ lenovo-wmi-gamezone
> |
> v
> lenovo-wmi-helpers
> ^
> |
> +------------ lenovo-wmi-other
>
> To make it clear, the name of the notifier chain is also renamed from
> `om_chain_head' to `tm_chain_head', indicating that it's used to query
> the current thermal mode.
>
> No functional change intended.
>
> Fixes: 6e38b9fcbfa3 ("platform/x86: lenovo: gamezone needs "other mode"")
I tagged it as a fix patch as stable kernel may also run into a broken
randconfig where CONFIG_LENOVO_WMI_TUNING=m/y is selected by
CONFIG_LENOVO_WMI_GAMEZONE=m/y but CONFIG_LENOVO_WMI_CAPDATA=n is still
selected by the randomizer, which is almost identical to [1].
Fix patches should be the very first patches in the series so that
backporting is less painful. See also my reply to the cover letter.
> Cc: stable@xxxxxxxxxxxxxxx
> Reported-by: kernel test robot <lkp@xxxxxxxxx>
> Closes: https://lore.kernel.org/oe-kbuild-all/202603252259.gHvJDyh3-lkp@xxxxxxxxx/
^ [1]
> Closes: https://lore.kernel.org/oe-kbuild-all/202603260302.X0NjQOda-lkp@xxxxxxxxx/
> Signed-off-by: Rong Zhang <i@xxxxxxxx>
> Signed-off-by: Derek J. Clark <derekjohn.clark@xxxxxxxxx>
> ---
> drivers/platform/x86/lenovo/Kconfig | 1 -
> drivers/platform/x86/lenovo/wmi-gamezone.c | 4 +-
> drivers/platform/x86/lenovo/wmi-helpers.c | 101 ++++++++++++++++++++
> drivers/platform/x86/lenovo/wmi-helpers.h | 8 ++
> drivers/platform/x86/lenovo/wmi-other.c | 104 +--------------------
> drivers/platform/x86/lenovo/wmi-other.h | 16 ----
> 6 files changed, 112 insertions(+), 122 deletions(-)
> delete mode 100644 drivers/platform/x86/lenovo/wmi-other.h
>
> diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig
> index 75a8b144b0da..b9a5d18caa1e 100644
> --- a/drivers/platform/x86/lenovo/Kconfig
> +++ b/drivers/platform/x86/lenovo/Kconfig
> @@ -252,7 +252,6 @@ config LENOVO_WMI_GAMEZONE
> select ACPI_PLATFORM_PROFILE
> select LENOVO_WMI_EVENTS
> select LENOVO_WMI_HELPERS
> - select LENOVO_WMI_TUNING
> help
> Say Y here if you have a WMI aware Lenovo Legion device and would like to use the
> platform-profile firmware interface to manage power usage.
> diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.c b/drivers/platform/x86/lenovo/wmi-gamezone.c
> index 602a48de1b4e..a614af8f08e8 100644
> --- a/drivers/platform/x86/lenovo/wmi-gamezone.c
> +++ b/drivers/platform/x86/lenovo/wmi-gamezone.c
> @@ -22,7 +22,6 @@
>
> #include "wmi-events.h"
> #include "wmi-helpers.h"
> -#include "wmi-other.h"
>
> #define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0"
>
> @@ -384,7 +383,7 @@ static int lwmi_gz_probe(struct wmi_device *wdev, const void *context)
> return ret;
>
> priv->mode_nb.notifier_call = lwmi_gz_mode_call;
> - return devm_lwmi_om_register_notifier(&wdev->dev, &priv->mode_nb);
> + return devm_lwmi_tm_register_notifier(&wdev->dev, &priv->mode_nb);
> }
>
> static const struct wmi_device_id lwmi_gz_id_table[] = {
> @@ -406,7 +405,6 @@ module_wmi_driver(lwmi_gz_driver);
>
> MODULE_IMPORT_NS("LENOVO_WMI_EVENTS");
> MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");
> -MODULE_IMPORT_NS("LENOVO_WMI_OTHER");
> MODULE_DEVICE_TABLE(wmi, lwmi_gz_id_table);
> MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@xxxxxxxxx>");
> MODULE_DESCRIPTION("Lenovo GameZone WMI Driver");
> diff --git a/drivers/platform/x86/lenovo/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c
> index 7379defac500..e1cf869224d2 100644
> --- a/drivers/platform/x86/lenovo/wmi-helpers.c
> +++ b/drivers/platform/x86/lenovo/wmi-helpers.c
> @@ -21,11 +21,15 @@
> #include <linux/errno.h>
> #include <linux/export.h>
> #include <linux/module.h>
> +#include <linux/notifier.h>
> #include <linux/unaligned.h>
> #include <linux/wmi.h>
>
> #include "wmi-helpers.h"
>
> +/* Thermal mode notifier chain. */
> +static BLOCKING_NOTIFIER_HEAD(tm_chain_head);
> +
> /**
> * lwmi_dev_evaluate_int() - Helper function for calling WMI methods that
> * return an integer.
> @@ -84,6 +88,103 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
> };
> EXPORT_SYMBOL_NS_GPL(lwmi_dev_evaluate_int, "LENOVO_WMI_HELPERS");
>
> +/**
> + * lwmi_tm_register_notifier() - Add a notifier to the blocking notifier chain
> + * @nb: The notifier_block struct to register
> + *
> + * Call blocking_notifier_chain_register to register the notifier block to the
> + * thermal mode notifier chain.
> + *
> + * Return: 0 on success, %-EEXIST on error.
> + */
> +int lwmi_tm_register_notifier(struct notifier_block *nb)
> +{
> + return blocking_notifier_chain_register(&tm_chain_head, nb);
> +}
> +EXPORT_SYMBOL_NS_GPL(lwmi_tm_register_notifier, "LENOVO_WMI_HELPERS");
> +
> +/**
> + * lwmi_tm_unregister_notifier() - Remove a notifier from the blocking notifier
> + * chain.
> + * @nb: The notifier_block struct to register
There is a typo. s/register/unregister/
(found by sashiko.dev)
https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com
> + *
> + * Call blocking_notifier_chain_unregister to unregister the notifier block from the
> + * thermal mode notifier chain.
> + *
> + * Return: 0 on success, %-ENOENT on error.
> + */
> +int lwmi_tm_unregister_notifier(struct notifier_block *nb)
> +{
> + return blocking_notifier_chain_unregister(&tm_chain_head, nb);
> +}
> +EXPORT_SYMBOL_NS_GPL(lwmi_tm_unregister_notifier, "LENOVO_WMI_HELPERS");
> +
> +/**
> + * devm_lwmi_tm_unregister_notifier() - Remove a notifier from the blocking
> + * notifier chain.
> + * @data: Void pointer to the notifier_block struct to register.
Ditto (found by sashiko.dev).
> + *
> + * Call lwmi_tm_unregister_notifier to unregister the notifier block from the
> + * thermal mode notifier chain.
> + *
> + * Return: 0 on success, %-ENOENT on error.
Remove it as it's a void function (found by sashiko.dev).
These typos have existed since its first appearance. I didn't catch them
when I was moving them.
Let's fix them as we are anyway touching them.
Thanks,
Rong
> + */
> +static void devm_lwmi_tm_unregister_notifier(void *data)
> +{
> + struct notifier_block *nb = data;
> +
> + lwmi_tm_unregister_notifier(nb);
> +}
> +
> +/**
> + * devm_lwmi_tm_register_notifier() - Add a notifier to the blocking notifier
> + * chain.
> + * @dev: The parent device of the notifier_block struct.
> + * @nb: The notifier_block struct to register
> + *
> + * Call lwmi_tm_register_notifier to register the notifier block to the
> + * thermal mode notifier chain. Then add devm_lwmi_tm_unregister_notifier
> + * as a device managed action to automatically unregister the notifier block
> + * upon parent device removal.
> + *
> + * Return: 0 on success, or an error code.
> + */
> +int devm_lwmi_tm_register_notifier(struct device *dev,
> + struct notifier_block *nb)
> +{
> + int ret;
> +
> + ret = lwmi_tm_register_notifier(nb);
> + if (ret < 0)
> + return ret;
> +
> + return devm_add_action_or_reset(dev, devm_lwmi_tm_unregister_notifier,
> + nb);
> +}
> +EXPORT_SYMBOL_NS_GPL(devm_lwmi_tm_register_notifier, "LENOVO_WMI_HELPERS");
> +
> +/**
> + * lwmi_tm_notifier_call() - Call functions for the notifier call chain.
> + * @mode: Pointer to a thermal mode enum to retrieve the data from.
> + *
> + * Call blocking_notifier_call_chain to retrieve the thermal mode from the
> + * lenovo-wmi-gamezone driver.
> + *
> + * Return: 0 on success, or an error code.
> + */
> +int lwmi_tm_notifier_call(enum thermal_mode *mode)
> +{
> + int ret;
> +
> + ret = blocking_notifier_call_chain(&tm_chain_head,
> + LWMI_GZ_GET_THERMAL_MODE, &mode);
> + if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK)
> + return -EINVAL;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(lwmi_tm_notifier_call, "LENOVO_WMI_HELPERS");
> +
> MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@xxxxxxxxx>");
> MODULE_DESCRIPTION("Lenovo WMI Helpers Driver");
> MODULE_LICENSE("GPL");
> diff --git a/drivers/platform/x86/lenovo/wmi-helpers.h b/drivers/platform/x86/lenovo/wmi-helpers.h
> index 3364d8e152ca..ed7db3ebba6c 100644
> --- a/drivers/platform/x86/lenovo/wmi-helpers.h
> +++ b/drivers/platform/x86/lenovo/wmi-helpers.h
> @@ -7,6 +7,8 @@
>
> #include <linux/types.h>
>
> +struct device;
> +struct notifier_block;
> struct wmi_device;
>
> struct wmi_method_args_32 {
> @@ -30,4 +32,10 @@ enum thermal_mode {
> int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
> unsigned char *buf, size_t size, u32 *retval);
>
> +int lwmi_tm_register_notifier(struct notifier_block *nb);
> +int lwmi_tm_unregister_notifier(struct notifier_block *nb);
> +int devm_lwmi_tm_register_notifier(struct device *dev,
> + struct notifier_block *nb);
> +int lwmi_tm_notifier_call(enum thermal_mode *mode);
> +
> #endif /* !_LENOVO_WMI_HELPERS_H_ */
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index e0633c42972c..d871ee02dfcb 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -41,7 +41,6 @@
> #include <linux/kobject.h>
> #include <linux/limits.h>
> #include <linux/module.h>
> -#include <linux/notifier.h>
> #include <linux/platform_profile.h>
> #include <linux/power_supply.h>
> #include <linux/types.h>
> @@ -52,7 +51,6 @@
> #include "wmi-capdata.h"
> #include "wmi-events.h"
> #include "wmi-helpers.h"
> -#include "wmi-other.h"
> #include "../firmware_attributes_class.h"
>
> #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
> @@ -110,7 +108,6 @@ enum lwmi_feature_id_gpu {
> #define LWMI_OM_SYSFS_NAME "lenovo-wmi-other"
> #define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
>
> -static BLOCKING_NOTIFIER_HEAD(om_chain_head);
> static DEFINE_IDA(lwmi_om_ida);
>
> enum attribute_property {
> @@ -138,7 +135,6 @@ struct lwmi_om_priv {
> struct device *hwmon_dev;
> struct device *fw_attr_dev;
> struct kset *fw_attr_kset;
> - struct notifier_block nb;
> struct wmi_device *wdev;
> int ida_id;
>
> @@ -979,102 +975,6 @@ struct capdata01_attr_group {
> struct tunable_attr_01 *tunable_attr;
> };
>
> -/**
> - * lwmi_om_register_notifier() - Add a notifier to the blocking notifier chain
> - * @nb: The notifier_block struct to register
> - *
> - * Call blocking_notifier_chain_register to register the notifier block to the
> - * lenovo-wmi-other driver notifier chain.
> - *
> - * Return: 0 on success, %-EEXIST on error.
> - */
> -int lwmi_om_register_notifier(struct notifier_block *nb)
> -{
> - return blocking_notifier_chain_register(&om_chain_head, nb);
> -}
> -EXPORT_SYMBOL_NS_GPL(lwmi_om_register_notifier, "LENOVO_WMI_OTHER");
> -
> -/**
> - * lwmi_om_unregister_notifier() - Remove a notifier from the blocking notifier
> - * chain.
> - * @nb: The notifier_block struct to register
> - *
> - * Call blocking_notifier_chain_unregister to unregister the notifier block from the
> - * lenovo-wmi-other driver notifier chain.
> - *
> - * Return: 0 on success, %-ENOENT on error.
> - */
> -int lwmi_om_unregister_notifier(struct notifier_block *nb)
> -{
> - return blocking_notifier_chain_unregister(&om_chain_head, nb);
> -}
> -EXPORT_SYMBOL_NS_GPL(lwmi_om_unregister_notifier, "LENOVO_WMI_OTHER");
> -
> -/**
> - * devm_lwmi_om_unregister_notifier() - Remove a notifier from the blocking
> - * notifier chain.
> - * @data: Void pointer to the notifier_block struct to register.
> - *
> - * Call lwmi_om_unregister_notifier to unregister the notifier block from the
> - * lenovo-wmi-other driver notifier chain.
> - *
> - * Return: 0 on success, %-ENOENT on error.
> - */
> -static void devm_lwmi_om_unregister_notifier(void *data)
> -{
> - struct notifier_block *nb = data;
> -
> - lwmi_om_unregister_notifier(nb);
> -}
> -
> -/**
> - * devm_lwmi_om_register_notifier() - Add a notifier to the blocking notifier
> - * chain.
> - * @dev: The parent device of the notifier_block struct.
> - * @nb: The notifier_block struct to register
> - *
> - * Call lwmi_om_register_notifier to register the notifier block to the
> - * lenovo-wmi-other driver notifier chain. Then add devm_lwmi_om_unregister_notifier
> - * as a device managed action to automatically unregister the notifier block
> - * upon parent device removal.
> - *
> - * Return: 0 on success, or an error code.
> - */
> -int devm_lwmi_om_register_notifier(struct device *dev,
> - struct notifier_block *nb)
> -{
> - int ret;
> -
> - ret = lwmi_om_register_notifier(nb);
> - if (ret < 0)
> - return ret;
> -
> - return devm_add_action_or_reset(dev, devm_lwmi_om_unregister_notifier,
> - nb);
> -}
> -EXPORT_SYMBOL_NS_GPL(devm_lwmi_om_register_notifier, "LENOVO_WMI_OTHER");
> -
> -/**
> - * lwmi_om_notifier_call() - Call functions for the notifier call chain.
> - * @mode: Pointer to a thermal mode enum to retrieve the data from.
> - *
> - * Call blocking_notifier_call_chain to retrieve the thermal mode from the
> - * lenovo-wmi-gamezone driver.
> - *
> - * Return: 0 on success, or an error code.
> - */
> -static int lwmi_om_notifier_call(enum thermal_mode *mode)
> -{
> - int ret;
> -
> - ret = blocking_notifier_call_chain(&om_chain_head,
> - LWMI_GZ_GET_THERMAL_MODE, &mode);
> - if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK)
> - return -EINVAL;
> -
> - return 0;
> -}
> -
> /* Attribute Methods */
>
> /**
> @@ -1178,7 +1078,7 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> u32 value;
> int ret;
>
> - ret = lwmi_om_notifier_call(&mode);
> + ret = lwmi_tm_notifier_call(&mode);
> if (ret)
> return ret;
>
> @@ -1237,7 +1137,7 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
> int retval;
> int ret;
>
> - ret = lwmi_om_notifier_call(&mode);
> + ret = lwmi_tm_notifier_call(&mode);
> if (ret)
> return ret;
>
> diff --git a/drivers/platform/x86/lenovo/wmi-other.h b/drivers/platform/x86/lenovo/wmi-other.h
> deleted file mode 100644
> index 8ebf5602bb99..000000000000
> --- a/drivers/platform/x86/lenovo/wmi-other.h
> +++ /dev/null
> @@ -1,16 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0-or-later */
> -
> -/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@xxxxxxxxx> */
> -
> -#ifndef _LENOVO_WMI_OTHER_H_
> -#define _LENOVO_WMI_OTHER_H_
> -
> -struct device;
> -struct notifier_block;
> -
> -int lwmi_om_register_notifier(struct notifier_block *nb);
> -int lwmi_om_unregister_notifier(struct notifier_block *nb);
> -int devm_lwmi_om_register_notifier(struct device *dev,
> - struct notifier_block *nb);
> -
> -#endif /* !_LENOVO_WMI_OTHER_H_ */