* [PATCH v10 01/16] platform/x86: lenovo-wmi-helpers: Fix memory leak in lwmi_dev_evaluate_int()
[not found] <20260412211121.2220556-1-derekjohn.clark@gmail.com>
@ 2026-04-12 21:11 ` Derek J. Clark
2026-04-12 21:11 ` [PATCH v10 02/16] platform/x86: lenovo-wmi-other: Balance IDA id allocation and free Derek J. Clark
` (5 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Derek J. Clark @ 2026-04-12 21:11 UTC (permalink / raw)
To: Ilpo Järvinen, Hans de Goede
Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
Derek J . Clark, platform-driver-x86, linux-kernel, stable
From: Rong Zhang <i@rong.moe>
lwmi_dev_evaluate_int() leaks output.pointer when retval == NULL (found
by sashiko.dev [1]).
Fix it by moving `ret_obj = output.pointer' outside of the `if (retval)'
block so that it is always freed by the __free cleanup callback.
No functional change intended.
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Fixes: e521d16e76cd ("platform/x86: Add lenovo-wmi-helpers")
Cc: stable@vger.kernel.org
Link: https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com [1]
Signed-off-by: Rong Zhang <i@rong.moe>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
drivers/platform/x86/lenovo/wmi-helpers.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/x86/lenovo/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c
index 7379defac500..018d7642e2bd 100644
--- a/drivers/platform/x86/lenovo/wmi-helpers.c
+++ b/drivers/platform/x86/lenovo/wmi-helpers.c
@@ -46,7 +46,6 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
unsigned char *buf, size_t size, u32 *retval)
{
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object *ret_obj __free(kfree) = NULL;
struct acpi_buffer input = { size, buf };
acpi_status status;
@@ -55,8 +54,9 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
if (ACPI_FAILURE(status))
return -EIO;
+ union acpi_object *ret_obj __free(kfree) = output.pointer;
+
if (retval) {
- ret_obj = output.pointer;
if (!ret_obj)
return -ENODATA;
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v10 02/16] platform/x86: lenovo-wmi-other: Balance IDA id allocation and free
[not found] <20260412211121.2220556-1-derekjohn.clark@gmail.com>
2026-04-12 21:11 ` [PATCH v10 01/16] platform/x86: lenovo-wmi-helpers: Fix memory leak in lwmi_dev_evaluate_int() Derek J. Clark
@ 2026-04-12 21:11 ` Derek J. Clark
2026-04-12 21:11 ` [PATCH v10 03/16] platform/x86: lenovo-wmi-other: Balance component bind and unbind Derek J. Clark
` (4 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Derek J. Clark @ 2026-04-12 21:11 UTC (permalink / raw)
To: Ilpo Järvinen, Hans de Goede
Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
Derek J . Clark, platform-driver-x86, linux-kernel, stable
From: Rong Zhang <i@rong.moe>
Currently, the IDA id is only freed on wmi-other device removal or
failure to create firmware-attributes device, kset, or attributes. It
leaks IDA ids if the wmi-other device is bound multiple times, as the
unbind callback never frees the previously allocated IDA id.
Additionally, if the wmi-other device has failed to create a
firmware-attributes device before it gets removed, the wmi-device
removal callback double frees the same IDA id.
These bugs were found by sashiko.dev [1].
Fix them by moving ida_free() into lwmi_om_fw_attr_remove() so it is
balanced with ida_alloc() in lwmi_om_fw_attr_add(). With them fixed,
properly set and utilize the validity of priv->ida_id to balance
firmware-attributes registration and removal, without relying on
propagating the registration error to the component framework, which is
more reliable and aligns with the hwmon device registration and removal
sequences.
No functional change intended.
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
Cc: stable@vger.kernel.org
Link: https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com [1]
Signed-off-by: Rong Zhang <i@rong.moe>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
v9:
- Invert err logic for when allocating IDA fails.
- Rename ida_alloc err goto from 'err' to 'err_no_ida' to disambiguate
from 'int err'.
---
drivers/platform/x86/lenovo/wmi-other.c | 36 ++++++++++++++-----------
1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index 6040f45aa2b0..be3309d74e03 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -957,17 +957,17 @@ static struct capdata01_attr_group cd01_attr_groups[] = {
/**
* lwmi_om_fw_attr_add() - Register all firmware_attributes_class members
* @priv: The Other Mode driver data.
- *
- * Return: Either 0, or an error code.
*/
-static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
+static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
{
unsigned int i;
int err;
- priv->ida_id = ida_alloc(&lwmi_om_ida, GFP_KERNEL);
- if (priv->ida_id < 0)
- return priv->ida_id;
+ err = ida_alloc(&lwmi_om_ida, GFP_KERNEL);
+ if (err < 0)
+ goto err_no_ida;
+
+ priv->ida_id = err;
priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL,
MKDEV(0, 0), NULL, "%s-%u",
@@ -993,7 +993,7 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
}
- return 0;
+ return;
err_remove_groups:
while (i--)
@@ -1007,7 +1007,12 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
err_free_ida:
ida_free(&lwmi_om_ida, priv->ida_id);
- return err;
+
+err_no_ida:
+ priv->ida_id = -EIDRM;
+
+ dev_warn(&priv->wdev->dev,
+ "failed to register firmware-attributes device: %d\n", err);
}
/**
@@ -1016,12 +1021,17 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
*/
static void lwmi_om_fw_attr_remove(struct lwmi_om_priv *priv)
{
+ if (priv->ida_id < 0)
+ return;
+
for (unsigned int i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++)
sysfs_remove_group(&priv->fw_attr_kset->kobj,
cd01_attr_groups[i].attr_group);
kset_unregister(priv->fw_attr_kset);
device_unregister(priv->fw_attr_dev);
+ ida_free(&lwmi_om_ida, priv->ida_id);
+ priv->ida_id = -EIDRM;
}
/* ======== Self (master: lenovo-wmi-other) ======== */
@@ -1063,7 +1073,9 @@ static int lwmi_om_master_bind(struct device *dev)
lwmi_om_fan_info_collect_cd00(priv);
- return lwmi_om_fw_attr_add(priv);
+ lwmi_om_fw_attr_add(priv);
+
+ return 0;
}
/**
@@ -1115,13 +1127,7 @@ static int lwmi_other_probe(struct wmi_device *wdev, const void *context)
static void lwmi_other_remove(struct wmi_device *wdev)
{
- struct lwmi_om_priv *priv = dev_get_drvdata(&wdev->dev);
-
component_master_del(&wdev->dev, &lwmi_om_master_ops);
-
- /* No IDA to free if the driver is never bound to its components. */
- if (priv->ida_id >= 0)
- ida_free(&lwmi_om_ida, priv->ida_id);
}
static const struct wmi_device_id lwmi_other_id_table[] = {
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v10 03/16] platform/x86: lenovo-wmi-other: Balance component bind and unbind
[not found] <20260412211121.2220556-1-derekjohn.clark@gmail.com>
2026-04-12 21:11 ` [PATCH v10 01/16] platform/x86: lenovo-wmi-helpers: Fix memory leak in lwmi_dev_evaluate_int() Derek J. Clark
2026-04-12 21:11 ` [PATCH v10 02/16] platform/x86: lenovo-wmi-other: Balance IDA id allocation and free Derek J. Clark
@ 2026-04-12 21:11 ` Derek J. Clark
2026-04-12 21:11 ` [PATCH v10 04/16] platform/x86: lenovo-wmi-other: Zero initialize WMI arguments Derek J. Clark
` (3 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Derek J. Clark @ 2026-04-12 21:11 UTC (permalink / raw)
To: Ilpo Järvinen, Hans de Goede
Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
Derek J . Clark, platform-driver-x86, linux-kernel, stable
From: Rong Zhang <i@rong.moe>
When lwmi_om_master_bind() fails, the master device's components are
left bound, with the aggregate device destroyed due to the failure
(found by sashiko.dev [1]).
Balance calls to component_bind_all() and component_unbind_all() when an
error is propagated to the component framework.
No functional change intended.
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
Cc: stable@vger.kernel.org
Link: https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com [1]
Signed-off-by: Rong Zhang <i@rong.moe>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
drivers/platform/x86/lenovo/wmi-other.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index be3309d74e03..a6be3463341c 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -1068,8 +1068,11 @@ static int lwmi_om_master_bind(struct device *dev)
priv->cd00_list = binder.cd00_list;
priv->cd01_list = binder.cd01_list;
- if (!priv->cd00_list || !priv->cd01_list)
+ if (!priv->cd00_list || !priv->cd01_list) {
+ component_unbind_all(dev, NULL);
+
return -ENODEV;
+ }
lwmi_om_fan_info_collect_cd00(priv);
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v10 04/16] platform/x86: lenovo-wmi-other: Zero initialize WMI arguments
[not found] <20260412211121.2220556-1-derekjohn.clark@gmail.com>
` (2 preceding siblings ...)
2026-04-12 21:11 ` [PATCH v10 03/16] platform/x86: lenovo-wmi-other: Balance component bind and unbind Derek J. Clark
@ 2026-04-12 21:11 ` Derek J. Clark
2026-04-12 21:11 ` [PATCH v10 05/16] platform/x86: lenovo-wmi-other: Fix tunable_attr_01 struct members Derek J. Clark
` (2 subsequent siblings)
6 siblings, 0 replies; 17+ messages in thread
From: Derek J. Clark @ 2026-04-12 21:11 UTC (permalink / raw)
To: Ilpo Järvinen, Hans de Goede
Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
Derek J . Clark, platform-driver-x86, linux-kernel, stable
Adds explicit initialization of wmi_method_args_32 declarations with
zero values to prevent uninitialized data from being sent to the device
BIOS when passed.
No functional change intended.
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Fixes: 22024ac5366f ("platform/x86: Add Lenovo Gamezone WMI Driver")
Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
Reported-by: Rong Zhang <i@rong.moe>
Closes: https://lore.kernel.org/platform-driver-x86/95c7e7b539dd0af41189c754fcd35cec5b6fe182.camel@rong.moe/
Cc: stable@vger.kernel.org
Reviewed-by: Rong Zhang <i@rong.moe>
Tested-by: Rong Zhang <i@rong.moe>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
v7:
- Include lwmi_gz_profile_set() fix as well.
---
drivers/platform/x86/lenovo/wmi-gamezone.c | 2 +-
drivers/platform/x86/lenovo/wmi-other.c | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.c b/drivers/platform/x86/lenovo/wmi-gamezone.c
index 381836d29a96..ca559e6c031d 100644
--- a/drivers/platform/x86/lenovo/wmi-gamezone.c
+++ b/drivers/platform/x86/lenovo/wmi-gamezone.c
@@ -203,7 +203,7 @@ static int lwmi_gz_profile_set(struct device *dev,
enum platform_profile_option profile)
{
struct lwmi_gz_priv *priv = dev_get_drvdata(dev);
- struct wmi_method_args_32 args;
+ struct wmi_method_args_32 args = {};
enum thermal_mode mode;
int ret;
diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index a6be3463341c..1e06b894cfcc 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -166,7 +166,7 @@ MODULE_PARM_DESC(relax_fan_constraint,
*/
static int lwmi_om_fan_get_set(struct lwmi_om_priv *priv, int channel, u32 *val, bool set)
{
- struct wmi_method_args_32 args;
+ struct wmi_method_args_32 args = {};
u32 method_id, retval;
int err;
@@ -773,7 +773,7 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
struct tunable_attr_01 *tunable_attr)
{
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
- struct wmi_method_args_32 args;
+ struct wmi_method_args_32 args = {};
struct capdata01 capdata;
enum thermal_mode mode;
u32 attribute_id;
@@ -836,7 +836,7 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
struct tunable_attr_01 *tunable_attr)
{
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
- struct wmi_method_args_32 args;
+ struct wmi_method_args_32 args = {};
enum thermal_mode mode;
u32 attribute_id;
int retval;
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v10 05/16] platform/x86: lenovo-wmi-other: Fix tunable_attr_01 struct members
[not found] <20260412211121.2220556-1-derekjohn.clark@gmail.com>
` (3 preceding siblings ...)
2026-04-12 21:11 ` [PATCH v10 04/16] platform/x86: lenovo-wmi-other: Zero initialize WMI arguments Derek J. Clark
@ 2026-04-12 21:11 ` Derek J. Clark
2026-04-12 21:11 ` [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices Derek J. Clark
2026-04-12 21:11 ` [PATCH v10 07/16] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other Derek J. Clark
6 siblings, 0 replies; 17+ messages in thread
From: Derek J. Clark @ 2026-04-12 21:11 UTC (permalink / raw)
To: Ilpo Järvinen, Hans de Goede
Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
Derek J . Clark, platform-driver-x86, linux-kernel, stable
In struct tunable_attr_01 the capdata pointer is unused and the size of
the id members is u32 when it should be u8. Fix these prior to adding
additional members.
No functional change intended.
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Fixes: e1a5fe662b59 ("platform/x86: Add Lenovo Capability Data 01 WMI Driver")
Cc: stable@vger.kernel.org
Reviewed-by: Rong Zhang <i@rong.moe>
Tested-by: Rong Zhang <i@rong.moe>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
drivers/platform/x86/lenovo/wmi-other.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index 1e06b894cfcc..50a03f5fd6ab 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -546,11 +546,10 @@ static void lwmi_om_fan_info_collect_cd_fan(struct device *dev, struct cd_list *
/* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */
struct tunable_attr_01 {
- struct capdata01 *capdata;
struct device *dev;
- u32 feature_id;
- u32 device_id;
- u32 type_id;
+ u8 feature_id;
+ u8 device_id;
+ u8 type_id;
};
static struct tunable_attr_01 ppt_pl1_spl = {
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
[not found] <20260412211121.2220556-1-derekjohn.clark@gmail.com>
` (4 preceding siblings ...)
2026-04-12 21:11 ` [PATCH v10 05/16] platform/x86: lenovo-wmi-other: Fix tunable_attr_01 struct members Derek J. Clark
@ 2026-04-12 21:11 ` Derek J. Clark
2026-04-30 14:01 ` Ilpo Järvinen
2026-04-12 21:11 ` [PATCH v10 07/16] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other Derek J. Clark
6 siblings, 1 reply; 17+ messages in thread
From: Derek J. Clark @ 2026-04-12 21:11 UTC (permalink / raw)
To: Ilpo Järvinen, Hans de Goede
Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
Derek J . Clark, platform-driver-x86, linux-kernel, stable
Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
if the attribute is supported by the hardware. Due to some poorly
implemented BIOS this is a multi-step sequence of events. This is
because:
- Some BIOS support getting the capability data from custom mode (0xff),
while others only support it in no-mode (0x00).
- Some BIOS support get/set for the current value from custom mode (0xff),
while others only support it in no-mode (0x00).
- Some BIOS report capability data for a method that is not fully
implemented.
- Some BIOS have methods fully implemented, but no complimentary
capability data.
To ensure we only expose fully implemented methods with corresponding
capability data, we check each outcome before reporting that an
attribute can be supported.
Checking for lwmi_is_attr_01_supported during remove is not done to
ensure that we don't attempt to call cd01 or send WMI events if one of
the interfaces being removed was the cause of the driver unloading.
Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
Reported-by: Kurt Borja <kuurtb@gmail.com>
Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
Cc: stable@vger.kernel.org
Reviewed-by: Rong Zhang <i@rong.moe>
Tested-by: Rong Zhang <i@rong.moe>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
v7:
- Move earlier in the series. This required dropping the use of
lwmi_attr_id as it will be added later.
- Add missing switch between cd_mode_id and cv_mode_id in
current_value_store.
v6:
- Zero initialize args in lwmi_is_attr_01_supported.
- Fix formatting.
v5:
- Move cv/cd_mode_id refrences from path 3/4.
- Add missing import for ARRAY_SIZE.
- Make lwmi_is_attr_01_supported return bool instead of u32.
- Various formatting fixes.
v4:
- Use for loop instead of backtrace gotos for checking if an attribute
is supported.
- Add include for dev_printk.
- Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
- Don't use symmetric cleanup of attributes in error states.
---
drivers/platform/x86/lenovo/wmi-gamezone.h | 1 +
drivers/platform/x86/lenovo/wmi-other.c | 114 ++++++++++++++++++---
2 files changed, 98 insertions(+), 17 deletions(-)
diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
index 6b163a5eeb95..ddb919cf6c36 100644
--- a/drivers/platform/x86/lenovo/wmi-gamezone.h
+++ b/drivers/platform/x86/lenovo/wmi-gamezone.h
@@ -10,6 +10,7 @@ enum gamezone_events_type {
};
enum thermal_mode {
+ LWMI_GZ_THERMAL_MODE_NONE = 0x00,
LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index 50a03f5fd6ab..29d062a1c6dc 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -550,6 +550,8 @@ struct tunable_attr_01 {
u8 feature_id;
u8 device_id;
u8 type_id;
+ u8 cd_mode_id; /* mode arg for searching capdata */
+ u8 cv_mode_id; /* mode arg for set/get current_value */
};
static struct tunable_attr_01 ppt_pl1_spl = {
@@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
struct wmi_method_args_32 args = {};
struct capdata01 capdata;
enum thermal_mode mode;
- u32 attribute_id;
u32 value;
int ret;
@@ -786,13 +787,12 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
return -EBUSY;
- attribute_id =
- FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
- FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
- FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
- FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
+ args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
+ FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
+ FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) |
+ FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
- ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
+ ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
if (ret)
return ret;
@@ -803,7 +803,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
if (value < capdata.min_value || value > capdata.max_value)
return -EINVAL;
- args.arg0 = attribute_id;
+ args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
+ FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
+ FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) |
+ FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
args.arg1 = value;
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
@@ -837,7 +840,6 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
struct wmi_method_args_32 args = {};
enum thermal_mode mode;
- u32 attribute_id;
int retval;
int ret;
@@ -845,13 +847,14 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
if (ret)
return ret;
- attribute_id =
- FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
- FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
- FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
- FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
+ /* If "no-mode" is the supported mode, ensure we never send current mode */
+ if (tunable_attr->cv_mode_id == LWMI_GZ_THERMAL_MODE_NONE)
+ mode = tunable_attr->cv_mode_id;
- args.arg0 = attribute_id;
+ args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
+ FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
+ FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
+ FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
(unsigned char *)&args, sizeof(args),
@@ -862,6 +865,81 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
return sysfs_emit(buf, "%d\n", retval);
}
+/**
+ * lwmi_attr_01_is_supported() - Determine if the given attribute is supported.
+ * @tunable_attr: The attribute to verify.
+ *
+ * First check if the attribute has a corresponding capdata01 table in the cd01
+ * module under the "custom" mode (0xff). If that is not present then check if
+ * there is a corresponding "no-mode" (0x00) entry. If either of those passes,
+ * check capdata->supported for values > 0. If capdata is available, attempt to
+ * determine the set/get mode for the current value property using a similar
+ * pattern. If the value returned by either custom or no-mode is 0, or we get
+ * an error, we assume that mode is not supported. If any of the above checks
+ * fail then the attribute is not fully supported.
+ *
+ * The probed cd_mode_id/cv_mode_id are stored on the tunable_attr for later
+ * reference.
+ *
+ * Return: bool.
+ */
+static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
+{
+ u8 modes[2] = { LWMI_GZ_THERMAL_MODE_CUSTOM, LWMI_GZ_THERMAL_MODE_NONE };
+ struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
+ struct wmi_method_args_32 args = {};
+ bool cd_mode_found = false;
+ bool cv_mode_found = false;
+ struct capdata01 capdata;
+ int retval, ret, i;
+
+ /* Determine tunable_attr->cd_mode_id*/
+ for (i = 0; i < ARRAY_SIZE(modes); i++) {
+ args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
+ FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
+ FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, modes[i]) |
+ FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
+
+ ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
+ if (ret || !capdata.supported)
+ continue;
+ tunable_attr->cd_mode_id = modes[i];
+ cd_mode_found = true;
+ break;
+ }
+
+ if (!cd_mode_found)
+ return cd_mode_found;
+
+ dev_dbg(tunable_attr->dev,
+ "cd_mode_id: %#010x\n", args.arg0);
+
+ /* Determine tunable_attr->cv_mode_id, returns 1 if supported*/
+ for (i = 0; i < ARRAY_SIZE(modes); i++) {
+ args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
+ FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
+ FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, modes[i]) |
+ FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
+
+ ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
+ (unsigned char *)&args, sizeof(args),
+ &retval);
+ if (ret || !retval)
+ continue;
+ tunable_attr->cv_mode_id = modes[i];
+ cv_mode_found = true;
+ break;
+ }
+
+ if (!cv_mode_found)
+ return cv_mode_found;
+
+ dev_dbg(tunable_attr->dev, "cv_mode_id: %#010x, attribute support level: %#010x\n",
+ args.arg0, capdata.supported);
+
+ return capdata.supported > 0 ? true : false;
+}
+
/* Lenovo WMI Other Mode Attribute macros */
#define __LWMI_ATTR_RO(_func, _name) \
{ \
@@ -985,12 +1063,14 @@ static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
}
for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) {
+ cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
+ if (!lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr))
+ continue;
+
err = sysfs_create_group(&priv->fw_attr_kset->kobj,
cd01_attr_groups[i].attr_group);
if (err)
goto err_remove_groups;
-
- cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
}
return;
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
2026-04-12 21:11 ` [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices Derek J. Clark
@ 2026-04-30 14:01 ` Ilpo Järvinen
2026-04-30 14:56 ` Derek J. Clark
0 siblings, 1 reply; 17+ messages in thread
From: Ilpo Järvinen @ 2026-04-30 14:01 UTC (permalink / raw)
To: Derek J. Clark
Cc: Hans de Goede, Mark Pearson, Armin Wolf, Jonathan Corbet,
Rong Zhang, Kurt Borja, platform-driver-x86, LKML, stable
On Sun, 12 Apr 2026, Derek J. Clark wrote:
> Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
> if the attribute is supported by the hardware. Due to some poorly
> implemented BIOS this is a multi-step sequence of events. This is
> because:
> - Some BIOS support getting the capability data from custom mode (0xff),
> while others only support it in no-mode (0x00).
> - Some BIOS support get/set for the current value from custom mode (0xff),
> while others only support it in no-mode (0x00).
> - Some BIOS report capability data for a method that is not fully
> implemented.
> - Some BIOS have methods fully implemented, but no complimentary
> capability data.
>
> To ensure we only expose fully implemented methods with corresponding
> capability data, we check each outcome before reporting that an
> attribute can be supported.
>
> Checking for lwmi_is_attr_01_supported during remove is not done to
> ensure that we don't attempt to call cd01 or send WMI events if one of
> the interfaces being removed was the cause of the driver unloading.
>
> Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
> Reported-by: Kurt Borja <kuurtb@gmail.com>
> Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
> Cc: stable@vger.kernel.org
> Reviewed-by: Rong Zhang <i@rong.moe>
> Tested-by: Rong Zhang <i@rong.moe>
> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> ---
> v7:
> - Move earlier in the series. This required dropping the use of
> lwmi_attr_id as it will be added later.
> - Add missing switch between cd_mode_id and cv_mode_id in
> current_value_store.
> v6:
> - Zero initialize args in lwmi_is_attr_01_supported.
> - Fix formatting.
> v5:
> - Move cv/cd_mode_id refrences from path 3/4.
> - Add missing import for ARRAY_SIZE.
> - Make lwmi_is_attr_01_supported return bool instead of u32.
> - Various formatting fixes.
> v4:
> - Use for loop instead of backtrace gotos for checking if an attribute
> is supported.
> - Add include for dev_printk.
> - Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
> - Don't use symmetric cleanup of attributes in error states.
> ---
> drivers/platform/x86/lenovo/wmi-gamezone.h | 1 +
> drivers/platform/x86/lenovo/wmi-other.c | 114 ++++++++++++++++++---
> 2 files changed, 98 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
> index 6b163a5eeb95..ddb919cf6c36 100644
> --- a/drivers/platform/x86/lenovo/wmi-gamezone.h
> +++ b/drivers/platform/x86/lenovo/wmi-gamezone.h
> @@ -10,6 +10,7 @@ enum gamezone_events_type {
> };
>
> enum thermal_mode {
> + LWMI_GZ_THERMAL_MODE_NONE = 0x00,
> LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
> LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
> LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index 50a03f5fd6ab..29d062a1c6dc 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -550,6 +550,8 @@ struct tunable_attr_01 {
> u8 feature_id;
> u8 device_id;
> u8 type_id;
> + u8 cd_mode_id; /* mode arg for searching capdata */
> + u8 cv_mode_id; /* mode arg for set/get current_value */
> };
>
> static struct tunable_attr_01 ppt_pl1_spl = {
> @@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> struct wmi_method_args_32 args = {};
> struct capdata01 capdata;
> enum thermal_mode mode;
> - u32 attribute_id;
> u32 value;
> int ret;
>
> @@ -786,13 +787,12 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
> return -EBUSY;
>
> - attribute_id =
> - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
> - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) |
> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>
> - ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
> + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
> if (ret)
> return ret;
>
> @@ -803,7 +803,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> if (value < capdata.min_value || value > capdata.max_value)
> return -EINVAL;
>
> - args.arg0 = attribute_id;
> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) |
> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
It's already repeated a few times and you're adding more in this patch.
We should have a helper function for this encoding as it seems to
repeat. That is, something that takes tunable_attr and mode as input
(the conversion of existing entries should be in own patch preceeding
this fix patch).
> args.arg1 = value;
>
> ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
> @@ -837,7 +840,6 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
> struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
> struct wmi_method_args_32 args = {};
> enum thermal_mode mode;
> - u32 attribute_id;
> int retval;
> int ret;
>
> @@ -845,13 +847,14 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
> if (ret)
> return ret;
>
> - attribute_id =
> - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
> - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> + /* If "no-mode" is the supported mode, ensure we never send current mode */
> + if (tunable_attr->cv_mode_id == LWMI_GZ_THERMAL_MODE_NONE)
> + mode = tunable_attr->cv_mode_id;
>
> - args.arg0 = attribute_id;
> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>
> ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
> (unsigned char *)&args, sizeof(args),
> @@ -862,6 +865,81 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
> return sysfs_emit(buf, "%d\n", retval);
> }
>
> +/**
> + * lwmi_attr_01_is_supported() - Determine if the given attribute is supported.
> + * @tunable_attr: The attribute to verify.
> + *
> + * First check if the attribute has a corresponding capdata01 table in the cd01
> + * module under the "custom" mode (0xff). If that is not present then check if
> + * there is a corresponding "no-mode" (0x00) entry. If either of those passes,
> + * check capdata->supported for values > 0. If capdata is available, attempt to
> + * determine the set/get mode for the current value property using a similar
> + * pattern. If the value returned by either custom or no-mode is 0, or we get
> + * an error, we assume that mode is not supported. If any of the above checks
> + * fail then the attribute is not fully supported.
> + *
> + * The probed cd_mode_id/cv_mode_id are stored on the tunable_attr for later
> + * reference.
> + *
> + * Return: bool.
> + */
> +static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
> +{
> + u8 modes[2] = { LWMI_GZ_THERMAL_MODE_CUSTOM, LWMI_GZ_THERMAL_MODE_NONE };
> + struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
> + struct wmi_method_args_32 args = {};
> + bool cd_mode_found = false;
> + bool cv_mode_found = false;
> + struct capdata01 capdata;
> + int retval, ret, i;
> +
> + /* Determine tunable_attr->cd_mode_id*/
> + for (i = 0; i < ARRAY_SIZE(modes); i++) {
> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, modes[i]) |
> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> +
> + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
> + if (ret || !capdata.supported)
> + continue;
> + tunable_attr->cd_mode_id = modes[i];
> + cd_mode_found = true;
> + break;
> + }
> +
> + if (!cd_mode_found)
> + return cd_mode_found;
> +
> + dev_dbg(tunable_attr->dev,
> + "cd_mode_id: %#010x\n", args.arg0);
> +
> + /* Determine tunable_attr->cv_mode_id, returns 1 if supported*/
> + for (i = 0; i < ARRAY_SIZE(modes); i++) {
> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, modes[i]) |
> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> +
> + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
> + (unsigned char *)&args, sizeof(args),
> + &retval);
> + if (ret || !retval)
> + continue;
> + tunable_attr->cv_mode_id = modes[i];
> + cv_mode_found = true;
> + break;
> + }
> +
> + if (!cv_mode_found)
> + return cv_mode_found;
> +
> + dev_dbg(tunable_attr->dev, "cv_mode_id: %#010x, attribute support level: %#010x\n",
> + args.arg0, capdata.supported);
> +
> + return capdata.supported > 0 ? true : false;
> +}
> +
> /* Lenovo WMI Other Mode Attribute macros */
> #define __LWMI_ATTR_RO(_func, _name) \
> { \
> @@ -985,12 +1063,14 @@ static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
> }
>
> for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) {
> + cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
> + if (!lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr))
> + continue;
> +
> err = sysfs_create_group(&priv->fw_attr_kset->kobj,
> cd01_attr_groups[i].attr_group);
> if (err)
> goto err_remove_groups;
> -
> - cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
> }
> return;
>
>
--
i.
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
2026-04-30 14:01 ` Ilpo Järvinen
@ 2026-04-30 14:56 ` Derek J. Clark
2026-04-30 18:10 ` Rong Zhang
2026-05-05 9:48 ` Ilpo Järvinen
0 siblings, 2 replies; 17+ messages in thread
From: Derek J. Clark @ 2026-04-30 14:56 UTC (permalink / raw)
To: Ilpo Järvinen
Cc: Hans de Goede, Mark Pearson, Armin Wolf, Jonathan Corbet,
Rong Zhang, Kurt Borja, platform-driver-x86, LKML, stable
On April 30, 2026 7:01:55 AM PDT, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> wrote:
>On Sun, 12 Apr 2026, Derek J. Clark wrote:
>
>> Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
>> if the attribute is supported by the hardware. Due to some poorly
>> implemented BIOS this is a multi-step sequence of events. This is
>> because:
>> - Some BIOS support getting the capability data from custom mode (0xff),
>> while others only support it in no-mode (0x00).
>> - Some BIOS support get/set for the current value from custom mode (0xff),
>> while others only support it in no-mode (0x00).
>> - Some BIOS report capability data for a method that is not fully
>> implemented.
>> - Some BIOS have methods fully implemented, but no complimentary
>> capability data.
>>
>> To ensure we only expose fully implemented methods with corresponding
>> capability data, we check each outcome before reporting that an
>> attribute can be supported.
>>
>> Checking for lwmi_is_attr_01_supported during remove is not done to
>> ensure that we don't attempt to call cd01 or send WMI events if one of
>> the interfaces being removed was the cause of the driver unloading.
>>
>> Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
>> Reported-by: Kurt Borja <kuurtb@gmail.com>
>> Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
>> Cc: stable@vger.kernel.org
>> Reviewed-by: Rong Zhang <i@rong.moe>
>> Tested-by: Rong Zhang <i@rong.moe>
>> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
>> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
>> ---
>> v7:
>> - Move earlier in the series. This required dropping the use of
>> lwmi_attr_id as it will be added later.
>> - Add missing switch between cd_mode_id and cv_mode_id in
>> current_value_store.
>> v6:
>> - Zero initialize args in lwmi_is_attr_01_supported.
>> - Fix formatting.
>> v5:
>> - Move cv/cd_mode_id refrences from path 3/4.
>> - Add missing import for ARRAY_SIZE.
>> - Make lwmi_is_attr_01_supported return bool instead of u32.
>> - Various formatting fixes.
>> v4:
>> - Use for loop instead of backtrace gotos for checking if an attribute
>> is supported.
>> - Add include for dev_printk.
>> - Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
>> - Don't use symmetric cleanup of attributes in error states.
>> ---
>> drivers/platform/x86/lenovo/wmi-gamezone.h | 1 +
>> drivers/platform/x86/lenovo/wmi-other.c | 114 ++++++++++++++++++---
>> 2 files changed, 98 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
>> index 6b163a5eeb95..ddb919cf6c36 100644
>> --- a/drivers/platform/x86/lenovo/wmi-gamezone.h
>> +++ b/drivers/platform/x86/lenovo/wmi-gamezone.h
>> @@ -10,6 +10,7 @@ enum gamezone_events_type {
>> };
>>
>> enum thermal_mode {
>> + LWMI_GZ_THERMAL_MODE_NONE = 0x00,
>> LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
>> LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
>> LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
>> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
>> index 50a03f5fd6ab..29d062a1c6dc 100644
>> --- a/drivers/platform/x86/lenovo/wmi-other.c
>> +++ b/drivers/platform/x86/lenovo/wmi-other.c
>> @@ -550,6 +550,8 @@ struct tunable_attr_01 {
>> u8 feature_id;
>> u8 device_id;
>> u8 type_id;
>> + u8 cd_mode_id; /* mode arg for searching capdata */
>> + u8 cv_mode_id; /* mode arg for set/get current_value */
>> };
>>
>> static struct tunable_attr_01 ppt_pl1_spl = {
>> @@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>> struct wmi_method_args_32 args = {};
>> struct capdata01 capdata;
>> enum thermal_mode mode;
>> - u32 attribute_id;
>> u32 value;
>> int ret;
>>
>> @@ -786,13 +787,12 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>> if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
>> return -EBUSY;
>>
>> - attribute_id =
>> - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
>> - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) |
>> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>>
>> - ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
>> + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
>> if (ret)
>> return ret;
>>
>> @@ -803,7 +803,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>> if (value < capdata.min_value || value > capdata.max_value)
>> return -EINVAL;
>>
>> - args.arg0 = attribute_id;
>> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) |
>> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>
>It's already repeated a few times and you're adding more in this patch.
>
>We should have a helper function for this encoding as it seems to
>repeat. That is, something that takes tunable_attr and mode as input
>(the conversion of existing entries should be in own patch preceeding
>this fix patch).
>
Hi Ilpo,
A function for that is added in patch 10, though it is slightly modified from that to be more flexible is tunable_attr isn't used (such as with the fan test attributes)
Originally I had that patch preceding any additions, but after discussing with Rong we felt like it would be easier for stable backports if all the fixes were upfront. I can certainly move it back if you still prefer.
Thanks,
Derek
>> args.arg1 = value;
>>
>> ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
>> @@ -837,7 +840,6 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
>> struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
>> struct wmi_method_args_32 args = {};
>> enum thermal_mode mode;
>> - u32 attribute_id;
>> int retval;
>> int ret;
>>
>> @@ -845,13 +847,14 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
>> if (ret)
>> return ret;
>>
>> - attribute_id =
>> - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
>> - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> + /* If "no-mode" is the supported mode, ensure we never send current mode */
>> + if (tunable_attr->cv_mode_id == LWMI_GZ_THERMAL_MODE_NONE)
>> + mode = tunable_attr->cv_mode_id;
>>
>> - args.arg0 = attribute_id;
>> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
>> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>>
>> ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
>> (unsigned char *)&args, sizeof(args),
>> @@ -862,6 +865,81 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
>> return sysfs_emit(buf, "%d\n", retval);
>> }
>>
>> +/**
>> + * lwmi_attr_01_is_supported() - Determine if the given attribute is supported.
>> + * @tunable_attr: The attribute to verify.
>> + *
>> + * First check if the attribute has a corresponding capdata01 table in the cd01
>> + * module under the "custom" mode (0xff). If that is not present then check if
>> + * there is a corresponding "no-mode" (0x00) entry. If either of those passes,
>> + * check capdata->supported for values > 0. If capdata is available, attempt to
>> + * determine the set/get mode for the current value property using a similar
>> + * pattern. If the value returned by either custom or no-mode is 0, or we get
>> + * an error, we assume that mode is not supported. If any of the above checks
>> + * fail then the attribute is not fully supported.
>> + *
>> + * The probed cd_mode_id/cv_mode_id are stored on the tunable_attr for later
>> + * reference.
>> + *
>> + * Return: bool.
>> + */
>> +static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
>> +{
>> + u8 modes[2] = { LWMI_GZ_THERMAL_MODE_CUSTOM, LWMI_GZ_THERMAL_MODE_NONE };
>> + struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
>> + struct wmi_method_args_32 args = {};
>> + bool cd_mode_found = false;
>> + bool cv_mode_found = false;
>> + struct capdata01 capdata;
>> + int retval, ret, i;
>> +
>> + /* Determine tunable_attr->cd_mode_id*/
>> + for (i = 0; i < ARRAY_SIZE(modes); i++) {
>> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, modes[i]) |
>> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> +
>> + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
>> + if (ret || !capdata.supported)
>> + continue;
>> + tunable_attr->cd_mode_id = modes[i];
>> + cd_mode_found = true;
>> + break;
>> + }
>> +
>> + if (!cd_mode_found)
>> + return cd_mode_found;
>> +
>> + dev_dbg(tunable_attr->dev,
>> + "cd_mode_id: %#010x\n", args.arg0);
>> +
>> + /* Determine tunable_attr->cv_mode_id, returns 1 if supported*/
>> + for (i = 0; i < ARRAY_SIZE(modes); i++) {
>> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, modes[i]) |
>> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> +
>> + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
>> + (unsigned char *)&args, sizeof(args),
>> + &retval);
>> + if (ret || !retval)
>> + continue;
>> + tunable_attr->cv_mode_id = modes[i];
>> + cv_mode_found = true;
>> + break;
>> + }
>> +
>> + if (!cv_mode_found)
>> + return cv_mode_found;
>> +
>> + dev_dbg(tunable_attr->dev, "cv_mode_id: %#010x, attribute support level: %#010x\n",
>> + args.arg0, capdata.supported);
>> +
>> + return capdata.supported > 0 ? true : false;
>> +}
>> +
>> /* Lenovo WMI Other Mode Attribute macros */
>> #define __LWMI_ATTR_RO(_func, _name) \
>> { \
>> @@ -985,12 +1063,14 @@ static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
>> }
>>
>> for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) {
>> + cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
>> + if (!lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr))
>> + continue;
>> +
>> err = sysfs_create_group(&priv->fw_attr_kset->kobj,
>> cd01_attr_groups[i].attr_group);
>> if (err)
>> goto err_remove_groups;
>> -
>> - cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
>> }
>> return;
>>
>>
>
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
2026-04-30 14:56 ` Derek J. Clark
@ 2026-04-30 18:10 ` Rong Zhang
2026-05-05 10:25 ` Ilpo Järvinen
2026-05-05 9:48 ` Ilpo Järvinen
1 sibling, 1 reply; 17+ messages in thread
From: Rong Zhang @ 2026-04-30 18:10 UTC (permalink / raw)
To: Derek J. Clark, Ilpo Järvinen
Cc: Hans de Goede, Mark Pearson, Armin Wolf, Jonathan Corbet,
Kurt Borja, platform-driver-x86, LKML, stable
Hi Derek,
On Thu, 2026-04-30 at 07:56 -0700, Derek J. Clark wrote:
> On April 30, 2026 7:01:55 AM PDT, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> wrote:
> > On Sun, 12 Apr 2026, Derek J. Clark wrote:
> >
> > > Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
> > > if the attribute is supported by the hardware. Due to some poorly
> > > implemented BIOS this is a multi-step sequence of events. This is
> > > because:
> > > - Some BIOS support getting the capability data from custom mode (0xff),
> > > while others only support it in no-mode (0x00).
> > > - Some BIOS support get/set for the current value from custom mode (0xff),
> > > while others only support it in no-mode (0x00).
> > > - Some BIOS report capability data for a method that is not fully
> > > implemented.
> > > - Some BIOS have methods fully implemented, but no complimentary
> > > capability data.
> > >
> > > To ensure we only expose fully implemented methods with corresponding
> > > capability data, we check each outcome before reporting that an
> > > attribute can be supported.
> > >
> > > Checking for lwmi_is_attr_01_supported during remove is not done to
> > > ensure that we don't attempt to call cd01 or send WMI events if one of
> > > the interfaces being removed was the cause of the driver unloading.
> > >
> > > Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
> > > Reported-by: Kurt Borja <kuurtb@gmail.com>
> > > Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
> > > Cc: stable@vger.kernel.org
> > > Reviewed-by: Rong Zhang <i@rong.moe>
> > > Tested-by: Rong Zhang <i@rong.moe>
> > > Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> > > Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> > > ---
> > > v7:
> > > - Move earlier in the series. This required dropping the use of
> > > lwmi_attr_id as it will be added later.
> > > - Add missing switch between cd_mode_id and cv_mode_id in
> > > current_value_store.
> > > v6:
> > > - Zero initialize args in lwmi_is_attr_01_supported.
> > > - Fix formatting.
> > > v5:
> > > - Move cv/cd_mode_id refrences from path 3/4.
> > > - Add missing import for ARRAY_SIZE.
> > > - Make lwmi_is_attr_01_supported return bool instead of u32.
> > > - Various formatting fixes.
> > > v4:
> > > - Use for loop instead of backtrace gotos for checking if an attribute
> > > is supported.
> > > - Add include for dev_printk.
> > > - Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
> > > - Don't use symmetric cleanup of attributes in error states.
> > > ---
> > > drivers/platform/x86/lenovo/wmi-gamezone.h | 1 +
> > > drivers/platform/x86/lenovo/wmi-other.c | 114 ++++++++++++++++++---
> > > 2 files changed, 98 insertions(+), 17 deletions(-)
> > >
> > > diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
> > > index 6b163a5eeb95..ddb919cf6c36 100644
> > > --- a/drivers/platform/x86/lenovo/wmi-gamezone.h
> > > +++ b/drivers/platform/x86/lenovo/wmi-gamezone.h
> > > @@ -10,6 +10,7 @@ enum gamezone_events_type {
> > > };
> > >
> > > enum thermal_mode {
> > > + LWMI_GZ_THERMAL_MODE_NONE = 0x00,
> > > LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
> > > LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
> > > LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
> > > diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> > > index 50a03f5fd6ab..29d062a1c6dc 100644
> > > --- a/drivers/platform/x86/lenovo/wmi-other.c
> > > +++ b/drivers/platform/x86/lenovo/wmi-other.c
> > > @@ -550,6 +550,8 @@ struct tunable_attr_01 {
> > > u8 feature_id;
> > > u8 device_id;
> > > u8 type_id;
> > > + u8 cd_mode_id; /* mode arg for searching capdata */
> > > + u8 cv_mode_id; /* mode arg for set/get current_value */
> > > };
> > >
> > > static struct tunable_attr_01 ppt_pl1_spl = {
> > > @@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> > > struct wmi_method_args_32 args = {};
> > > struct capdata01 capdata;
> > > enum thermal_mode mode;
> > > - u32 attribute_id;
> > > u32 value;
> > > int ret;
> > >
> > > @@ -786,13 +787,12 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> > > if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
> > > return -EBUSY;
> > >
> > > - attribute_id =
> > > - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> > > - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> > > - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
> > > - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) |
> > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> > >
> > > - ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
> > > + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
> > > if (ret)
> > > return ret;
> > >
> > > @@ -803,7 +803,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> > > if (value < capdata.min_value || value > capdata.max_value)
> > > return -EINVAL;
> > >
> > > - args.arg0 = attribute_id;
> > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) |
> > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> >
> > It's already repeated a few times and you're adding more in this patch.
> >
> > We should have a helper function for this encoding as it seems to
> > repeat. That is, something that takes tunable_attr and mode as input
> > (the conversion of existing entries should be in own patch preceeding
> > this fix patch).
> >
>
> Hi Ilpo,
>
> A function for that is added in patch 10, though it is slightly modified from that to be more flexible is tunable_attr isn't used (such as with the fan test attributes)
>
> Originally I had that patch preceding any additions, but after discussing with Rong we felt like it would be easier for stable backports if all the fixes were upfront. I can certainly move it back if you still prefer.
Moving it back is OK for me, too.
I think exposing non-fully-functioning fw-attrs on stable/LTS kernels
should be acceptable as long as reading/writing these attributes doesn't
break anything, which is exactly the case now (i.e., without this
patch).
Thanks a lot for your hard work in this series,
Rong
>
> Thanks,
> Derek
>
>
> > > args.arg1 = value;
> > >
> > > ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
> > > @@ -837,7 +840,6 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
> > > struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
> > > struct wmi_method_args_32 args = {};
> > > enum thermal_mode mode;
> > > - u32 attribute_id;
> > > int retval;
> > > int ret;
> > >
> > > @@ -845,13 +847,14 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
> > > if (ret)
> > > return ret;
> > >
> > > - attribute_id =
> > > - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> > > - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> > > - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
> > > - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> > > + /* If "no-mode" is the supported mode, ensure we never send current mode */
> > > + if (tunable_attr->cv_mode_id == LWMI_GZ_THERMAL_MODE_NONE)
> > > + mode = tunable_attr->cv_mode_id;
> > >
> > > - args.arg0 = attribute_id;
> > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
> > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> > >
> > > ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
> > > (unsigned char *)&args, sizeof(args),
> > > @@ -862,6 +865,81 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
> > > return sysfs_emit(buf, "%d\n", retval);
> > > }
> > >
> > > +/**
> > > + * lwmi_attr_01_is_supported() - Determine if the given attribute is supported.
> > > + * @tunable_attr: The attribute to verify.
> > > + *
> > > + * First check if the attribute has a corresponding capdata01 table in the cd01
> > > + * module under the "custom" mode (0xff). If that is not present then check if
> > > + * there is a corresponding "no-mode" (0x00) entry. If either of those passes,
> > > + * check capdata->supported for values > 0. If capdata is available, attempt to
> > > + * determine the set/get mode for the current value property using a similar
> > > + * pattern. If the value returned by either custom or no-mode is 0, or we get
> > > + * an error, we assume that mode is not supported. If any of the above checks
> > > + * fail then the attribute is not fully supported.
> > > + *
> > > + * The probed cd_mode_id/cv_mode_id are stored on the tunable_attr for later
> > > + * reference.
> > > + *
> > > + * Return: bool.
> > > + */
> > > +static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
> > > +{
> > > + u8 modes[2] = { LWMI_GZ_THERMAL_MODE_CUSTOM, LWMI_GZ_THERMAL_MODE_NONE };
> > > + struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
> > > + struct wmi_method_args_32 args = {};
> > > + bool cd_mode_found = false;
> > > + bool cv_mode_found = false;
> > > + struct capdata01 capdata;
> > > + int retval, ret, i;
> > > +
> > > + /* Determine tunable_attr->cd_mode_id*/
> > > + for (i = 0; i < ARRAY_SIZE(modes); i++) {
> > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, modes[i]) |
> > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> > > +
> > > + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
> > > + if (ret || !capdata.supported)
> > > + continue;
> > > + tunable_attr->cd_mode_id = modes[i];
> > > + cd_mode_found = true;
> > > + break;
> > > + }
> > > +
> > > + if (!cd_mode_found)
> > > + return cd_mode_found;
> > > +
> > > + dev_dbg(tunable_attr->dev,
> > > + "cd_mode_id: %#010x\n", args.arg0);
> > > +
> > > + /* Determine tunable_attr->cv_mode_id, returns 1 if supported*/
> > > + for (i = 0; i < ARRAY_SIZE(modes); i++) {
> > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, modes[i]) |
> > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> > > +
> > > + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
> > > + (unsigned char *)&args, sizeof(args),
> > > + &retval);
> > > + if (ret || !retval)
> > > + continue;
> > > + tunable_attr->cv_mode_id = modes[i];
> > > + cv_mode_found = true;
> > > + break;
> > > + }
> > > +
> > > + if (!cv_mode_found)
> > > + return cv_mode_found;
> > > +
> > > + dev_dbg(tunable_attr->dev, "cv_mode_id: %#010x, attribute support level: %#010x\n",
> > > + args.arg0, capdata.supported);
> > > +
> > > + return capdata.supported > 0 ? true : false;
> > > +}
> > > +
> > > /* Lenovo WMI Other Mode Attribute macros */
> > > #define __LWMI_ATTR_RO(_func, _name) \
> > > { \
> > > @@ -985,12 +1063,14 @@ static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
> > > }
> > >
> > > for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) {
> > > + cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
> > > + if (!lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr))
> > > + continue;
> > > +
> > > err = sysfs_create_group(&priv->fw_attr_kset->kobj,
> > > cd01_attr_groups[i].attr_group);
> > > if (err)
> > > goto err_remove_groups;
> > > -
> > > - cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
> > > }
> > > return;
> > >
> > >
> >
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
2026-04-30 18:10 ` Rong Zhang
@ 2026-05-05 10:25 ` Ilpo Järvinen
2026-05-05 17:20 ` Rong Zhang
0 siblings, 1 reply; 17+ messages in thread
From: Ilpo Järvinen @ 2026-05-05 10:25 UTC (permalink / raw)
To: Rong Zhang
Cc: Derek J. Clark, Hans de Goede, Mark Pearson, Armin Wolf,
Jonathan Corbet, Kurt Borja, platform-driver-x86, LKML, stable
[-- Attachment #1: Type: text/plain, Size: 7432 bytes --]
On Fri, 1 May 2026, Rong Zhang wrote:
> On Thu, 2026-04-30 at 07:56 -0700, Derek J. Clark wrote:
> > On April 30, 2026 7:01:55 AM PDT, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> wrote:
> > > On Sun, 12 Apr 2026, Derek J. Clark wrote:
> > >
> > > > Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
> > > > if the attribute is supported by the hardware. Due to some poorly
> > > > implemented BIOS this is a multi-step sequence of events. This is
> > > > because:
> > > > - Some BIOS support getting the capability data from custom mode (0xff),
> > > > while others only support it in no-mode (0x00).
> > > > - Some BIOS support get/set for the current value from custom mode (0xff),
> > > > while others only support it in no-mode (0x00).
> > > > - Some BIOS report capability data for a method that is not fully
> > > > implemented.
> > > > - Some BIOS have methods fully implemented, but no complimentary
> > > > capability data.
> > > >
> > > > To ensure we only expose fully implemented methods with corresponding
> > > > capability data, we check each outcome before reporting that an
> > > > attribute can be supported.
> > > >
> > > > Checking for lwmi_is_attr_01_supported during remove is not done to
> > > > ensure that we don't attempt to call cd01 or send WMI events if one of
> > > > the interfaces being removed was the cause of the driver unloading.
> > > >
> > > > Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
> > > > Reported-by: Kurt Borja <kuurtb@gmail.com>
> > > > Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
> > > > Cc: stable@vger.kernel.org
> > > > Reviewed-by: Rong Zhang <i@rong.moe>
> > > > Tested-by: Rong Zhang <i@rong.moe>
> > > > Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> > > > Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> > > > ---
> > > > v7:
> > > > - Move earlier in the series. This required dropping the use of
> > > > lwmi_attr_id as it will be added later.
> > > > - Add missing switch between cd_mode_id and cv_mode_id in
> > > > current_value_store.
> > > > v6:
> > > > - Zero initialize args in lwmi_is_attr_01_supported.
> > > > - Fix formatting.
> > > > v5:
> > > > - Move cv/cd_mode_id refrences from path 3/4.
> > > > - Add missing import for ARRAY_SIZE.
> > > > - Make lwmi_is_attr_01_supported return bool instead of u32.
> > > > - Various formatting fixes.
> > > > v4:
> > > > - Use for loop instead of backtrace gotos for checking if an attribute
> > > > is supported.
> > > > - Add include for dev_printk.
> > > > - Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
> > > > - Don't use symmetric cleanup of attributes in error states.
> > > > ---
> > > > drivers/platform/x86/lenovo/wmi-gamezone.h | 1 +
> > > > drivers/platform/x86/lenovo/wmi-other.c | 114 ++++++++++++++++++---
> > > > 2 files changed, 98 insertions(+), 17 deletions(-)
> > > >
> > > > diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
> > > > index 6b163a5eeb95..ddb919cf6c36 100644
> > > > --- a/drivers/platform/x86/lenovo/wmi-gamezone.h
> > > > +++ b/drivers/platform/x86/lenovo/wmi-gamezone.h
> > > > @@ -10,6 +10,7 @@ enum gamezone_events_type {
> > > > };
> > > >
> > > > enum thermal_mode {
> > > > + LWMI_GZ_THERMAL_MODE_NONE = 0x00,
> > > > LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
> > > > LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
> > > > LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
> > > > diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> > > > index 50a03f5fd6ab..29d062a1c6dc 100644
> > > > --- a/drivers/platform/x86/lenovo/wmi-other.c
> > > > +++ b/drivers/platform/x86/lenovo/wmi-other.c
> > > > @@ -550,6 +550,8 @@ struct tunable_attr_01 {
> > > > u8 feature_id;
> > > > u8 device_id;
> > > > u8 type_id;
> > > > + u8 cd_mode_id; /* mode arg for searching capdata */
> > > > + u8 cv_mode_id; /* mode arg for set/get current_value */
> > > > };
> > > >
> > > > static struct tunable_attr_01 ppt_pl1_spl = {
> > > > @@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> > > > struct wmi_method_args_32 args = {};
> > > > struct capdata01 capdata;
> > > > enum thermal_mode mode;
> > > > - u32 attribute_id;
> > > > u32 value;
> > > > int ret;
> > > >
> > > > @@ -786,13 +787,12 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> > > > if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
> > > > return -EBUSY;
> > > >
> > > > - attribute_id =
> > > > - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> > > > - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> > > > - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
> > > > - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> > > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> > > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> > > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) |
> > > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> > > >
> > > > - ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
> > > > + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
> > > > if (ret)
> > > > return ret;
> > > >
> > > > @@ -803,7 +803,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> > > > if (value < capdata.min_value || value > capdata.max_value)
> > > > return -EINVAL;
> > > >
> > > > - args.arg0 = attribute_id;
> > > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> > > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> > > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) |
> > > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> > >
> > > It's already repeated a few times and you're adding more in this patch.
> > >
> > > We should have a helper function for this encoding as it seems to
> > > repeat. That is, something that takes tunable_attr and mode as input
> > > (the conversion of existing entries should be in own patch preceeding
> > > this fix patch).
> > >
> >
> > Hi Ilpo,
> >
> > A function for that is added in patch 10, though it is slightly modified from that to be more flexible is tunable_attr isn't used (such as with the fan test attributes)
> >
> > Originally I had that patch preceding any additions, but after
> > discussing with Rong we felt like it would be easier for stable
> > backports if all the fixes were upfront. I can certainly move it back
> > if you still prefer.
>
> Moving it back is OK for me, too.
>
> I think exposing non-fully-functioning fw-attrs on stable/LTS kernels
> should be acceptable as long as reading/writing these attributes doesn't
> break anything, which is exactly the case now (i.e., without this
> patch).
How would refactoring the code into a helper result in changing stable
interface?
Or did you perhaps move to talk about something entirely else (and I
ended up losing the context)?
> Thanks a lot for your hard work in this series,
--
i.
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
2026-05-05 10:25 ` Ilpo Järvinen
@ 2026-05-05 17:20 ` Rong Zhang
2026-05-05 17:38 ` Derek J. Clark
2026-05-05 18:42 ` Ilpo Järvinen
0 siblings, 2 replies; 17+ messages in thread
From: Rong Zhang @ 2026-05-05 17:20 UTC (permalink / raw)
To: Ilpo Järvinen
Cc: Derek J. Clark, Hans de Goede, Mark Pearson, Armin Wolf,
Jonathan Corbet, Kurt Borja, platform-driver-x86, LKML, stable
Hi Ilpo,
于 2026年5月5日 GMT+08:00 18:25:34,"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> 写道:
>On Fri, 1 May 2026, Rong Zhang wrote:
>> On Thu, 2026-04-30 at 07:56 -0700, Derek J. Clark wrote:
>> > On April 30, 2026 7:01:55 AM PDT, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> wrote:
>> > > On Sun, 12 Apr 2026, Derek J. Clark wrote:
>> > >
>> > > > Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
>> > > > if the attribute is supported by the hardware. Due to some poorly
>> > > > implemented BIOS this is a multi-step sequence of events. This is
>> > > > because:
>> > > > - Some BIOS support getting the capability data from custom mode (0xff),
>> > > > while others only support it in no-mode (0x00).
>> > > > - Some BIOS support get/set for the current value from custom mode (0xff),
>> > > > while others only support it in no-mode (0x00).
>> > > > - Some BIOS report capability data for a method that is not fully
>> > > > implemented.
>> > > > - Some BIOS have methods fully implemented, but no complimentary
>> > > > capability data.
>> > > >
>> > > > To ensure we only expose fully implemented methods with corresponding
>> > > > capability data, we check each outcome before reporting that an
>> > > > attribute can be supported.
>> > > >
>> > > > Checking for lwmi_is_attr_01_supported during remove is not done to
>> > > > ensure that we don't attempt to call cd01 or send WMI events if one of
>> > > > the interfaces being removed was the cause of the driver unloading.
>> > > >
>> > > > Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
>> > > > Reported-by: Kurt Borja <kuurtb@gmail.com>
>> > > > Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
>> > > > Cc: stable@vger.kernel.org
>> > > > Reviewed-by: Rong Zhang <i@rong.moe>
>> > > > Tested-by: Rong Zhang <i@rong.moe>
>> > > > Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
>> > > > Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
>> > > > ---
>> > > > v7:
>> > > > - Move earlier in the series. This required dropping the use of
>> > > > lwmi_attr_id as it will be added later.
>> > > > - Add missing switch between cd_mode_id and cv_mode_id in
>> > > > current_value_store.
>> > > > v6:
>> > > > - Zero initialize args in lwmi_is_attr_01_supported.
>> > > > - Fix formatting.
>> > > > v5:
>> > > > - Move cv/cd_mode_id refrences from path 3/4.
>> > > > - Add missing import for ARRAY_SIZE.
>> > > > - Make lwmi_is_attr_01_supported return bool instead of u32.
>> > > > - Various formatting fixes.
>> > > > v4:
>> > > > - Use for loop instead of backtrace gotos for checking if an attribute
>> > > > is supported.
>> > > > - Add include for dev_printk.
>> > > > - Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
>> > > > - Don't use symmetric cleanup of attributes in error states.
>> > > > ---
>> > > > drivers/platform/x86/lenovo/wmi-gamezone.h | 1 +
>> > > > drivers/platform/x86/lenovo/wmi-other.c | 114 ++++++++++++++++++---
>> > > > 2 files changed, 98 insertions(+), 17 deletions(-)
>> > > >
>> > > > diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
>> > > > index 6b163a5eeb95..ddb919cf6c36 100644
>> > > > --- a/drivers/platform/x86/lenovo/wmi-gamezone.h
>> > > > +++ b/drivers/platform/x86/lenovo/wmi-gamezone.h
>> > > > @@ -10,6 +10,7 @@ enum gamezone_events_type {
>> > > > };
>> > > >
>> > > > enum thermal_mode {
>> > > > + LWMI_GZ_THERMAL_MODE_NONE = 0x00,
>> > > > LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
>> > > > LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
>> > > > LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
>> > > > diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
>> > > > index 50a03f5fd6ab..29d062a1c6dc 100644
>> > > > --- a/drivers/platform/x86/lenovo/wmi-other.c
>> > > > +++ b/drivers/platform/x86/lenovo/wmi-other.c
>> > > > @@ -550,6 +550,8 @@ struct tunable_attr_01 {
>> > > > u8 feature_id;
>> > > > u8 device_id;
>> > > > u8 type_id;
>> > > > + u8 cd_mode_id; /* mode arg for searching capdata */
>> > > > + u8 cv_mode_id; /* mode arg for set/get current_value */
>> > > > };
>> > > >
>> > > > static struct tunable_attr_01 ppt_pl1_spl = {
>> > > > @@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>> > > > struct wmi_method_args_32 args = {};
>> > > > struct capdata01 capdata;
>> > > > enum thermal_mode mode;
>> > > > - u32 attribute_id;
>> > > > u32 value;
>> > > > int ret;
>> > > >
>> > > > @@ -786,13 +787,12 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>> > > > if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
>> > > > return -EBUSY;
>> > > >
>> > > > - attribute_id =
>> > > > - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> > > > - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> > > > - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
>> > > > - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> > > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> > > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> > > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) |
>> > > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> > > >
>> > > > - ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
>> > > > + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
>> > > > if (ret)
>> > > > return ret;
>> > > >
>> > > > @@ -803,7 +803,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>> > > > if (value < capdata.min_value || value > capdata.max_value)
>> > > > return -EINVAL;
>> > > >
>> > > > - args.arg0 = attribute_id;
>> > > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> > > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> > > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) |
>> > > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> > >
>> > > It's already repeated a few times and you're adding more in this patch.
>> > >
>> > > We should have a helper function for this encoding as it seems to
>> > > repeat. That is, something that takes tunable_attr and mode as input
>> > > (the conversion of existing entries should be in own patch preceeding
>> > > this fix patch).
>> > >
>> >
>> > Hi Ilpo,
>> >
>> > A function for that is added in patch 10, though it is slightly modified from that to be more flexible is tunable_attr isn't used (such as with the fan test attributes)
>> >
>> > Originally I had that patch preceding any additions, but after
>> > discussing with Rong we felt like it would be easier for stable
>> > backports if all the fixes were upfront. I can certainly move it back
>> > if you still prefer.
>>
>> Moving it back is OK for me, too.
>>
>> I think exposing non-fully-functioning fw-attrs on stable/LTS kernels
>> should be acceptable as long as reading/writing these attributes doesn't
>> break anything, which is exactly the case now (i.e., without this
>> patch).
>
>How would refactoring the code into a helper result in changing stable
>interface?
>
>Or did you perhaps move to talk about something entirely else (and I
>ended up losing the context)?
I meant the *current* LTS/stable state is acceptable for me as reading/writing these attributes doesn't break anything. Moving it back would be OK for me even if it made this patch unable to be backported (though unlikely, since moving it back makes the patch clearer as you've said). In other words, it doesn't matter for me whether the patch is backported or not.
Some context: I didn't ask Derek to add Fixes: tag to this patch or move it. I asked him to rearrange other patches to make them backportable.
Sorry for causing misunderstandings.
Thanks,
Rong
>
>> Thanks a lot for your hard work in this series,
>
>
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
2026-05-05 17:20 ` Rong Zhang
@ 2026-05-05 17:38 ` Derek J. Clark
2026-05-05 18:42 ` Ilpo Järvinen
1 sibling, 0 replies; 17+ messages in thread
From: Derek J. Clark @ 2026-05-05 17:38 UTC (permalink / raw)
To: Rong Zhang, Ilpo Järvinen
Cc: Hans de Goede, Mark Pearson, Armin Wolf, Jonathan Corbet,
Kurt Borja, platform-driver-x86, LKML, stable
On May 5, 2026 10:20:15 AM PDT, Rong Zhang <i@rong.moe> wrote:
>Hi Ilpo,
>
>于 2026年5月5日 GMT+08:00 18:25:34,"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> 写道:
>>On Fri, 1 May 2026, Rong Zhang wrote:
>>> On Thu, 2026-04-30 at 07:56 -0700, Derek J. Clark wrote:
>>> > On April 30, 2026 7:01:55 AM PDT, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> wrote:
>>> > > On Sun, 12 Apr 2026, Derek J. Clark wrote:
>>> > >
>>> > > > Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
>>> > > > if the attribute is supported by the hardware. Due to some poorly
>>> > > > implemented BIOS this is a multi-step sequence of events. This is
>>> > > > because:
>>> > > > - Some BIOS support getting the capability data from custom mode (0xff),
>>> > > > while others only support it in no-mode (0x00).
>>> > > > - Some BIOS support get/set for the current value from custom mode (0xff),
>>> > > > while others only support it in no-mode (0x00).
>>> > > > - Some BIOS report capability data for a method that is not fully
>>> > > > implemented.
>>> > > > - Some BIOS have methods fully implemented, but no complimentary
>>> > > > capability data.
>>> > > >
>>> > > > To ensure we only expose fully implemented methods with corresponding
>>> > > > capability data, we check each outcome before reporting that an
>>> > > > attribute can be supported.
>>> > > >
>>> > > > Checking for lwmi_is_attr_01_supported during remove is not done to
>>> > > > ensure that we don't attempt to call cd01 or send WMI events if one of
>>> > > > the interfaces being removed was the cause of the driver unloading.
>>> > > >
>>> > > > Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
>>> > > > Reported-by: Kurt Borja <kuurtb@gmail.com>
>>> > > > Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
>>> > > > Cc: stable@vger.kernel.org
>>> > > > Reviewed-by: Rong Zhang <i@rong.moe>
>>> > > > Tested-by: Rong Zhang <i@rong.moe>
>>> > > > Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
>>> > > > Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
>>> > > > ---
>>> > > > v7:
>>> > > > - Move earlier in the series. This required dropping the use of
>>> > > > lwmi_attr_id as it will be added later.
>>> > > > - Add missing switch between cd_mode_id and cv_mode_id in
>>> > > > current_value_store.
>>> > > > v6:
>>> > > > - Zero initialize args in lwmi_is_attr_01_supported.
>>> > > > - Fix formatting.
>>> > > > v5:
>>> > > > - Move cv/cd_mode_id refrences from path 3/4.
>>> > > > - Add missing import for ARRAY_SIZE.
>>> > > > - Make lwmi_is_attr_01_supported return bool instead of u32.
>>> > > > - Various formatting fixes.
>>> > > > v4:
>>> > > > - Use for loop instead of backtrace gotos for checking if an attribute
>>> > > > is supported.
>>> > > > - Add include for dev_printk.
>>> > > > - Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
>>> > > > - Don't use symmetric cleanup of attributes in error states.
>>> > > > ---
>>> > > > drivers/platform/x86/lenovo/wmi-gamezone.h | 1 +
>>> > > > drivers/platform/x86/lenovo/wmi-other.c | 114 ++++++++++++++++++---
>>> > > > 2 files changed, 98 insertions(+), 17 deletions(-)
>>> > > >
>>> > > > diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
>>> > > > index 6b163a5eeb95..ddb919cf6c36 100644
>>> > > > --- a/drivers/platform/x86/lenovo/wmi-gamezone.h
>>> > > > +++ b/drivers/platform/x86/lenovo/wmi-gamezone.h
>>> > > > @@ -10,6 +10,7 @@ enum gamezone_events_type {
>>> > > > };
>>> > > >
>>> > > > enum thermal_mode {
>>> > > > + LWMI_GZ_THERMAL_MODE_NONE = 0x00,
>>> > > > LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
>>> > > > LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
>>> > > > LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
>>> > > > diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
>>> > > > index 50a03f5fd6ab..29d062a1c6dc 100644
>>> > > > --- a/drivers/platform/x86/lenovo/wmi-other.c
>>> > > > +++ b/drivers/platform/x86/lenovo/wmi-other.c
>>> > > > @@ -550,6 +550,8 @@ struct tunable_attr_01 {
>>> > > > u8 feature_id;
>>> > > > u8 device_id;
>>> > > > u8 type_id;
>>> > > > + u8 cd_mode_id; /* mode arg for searching capdata */
>>> > > > + u8 cv_mode_id; /* mode arg for set/get current_value */
>>> > > > };
>>> > > >
>>> > > > static struct tunable_attr_01 ppt_pl1_spl = {
>>> > > > @@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>>> > > > struct wmi_method_args_32 args = {};
>>> > > > struct capdata01 capdata;
>>> > > > enum thermal_mode mode;
>>> > > > - u32 attribute_id;
>>> > > > u32 value;
>>> > > > int ret;
>>> > > >
>>> > > > @@ -786,13 +787,12 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>>> > > > if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
>>> > > > return -EBUSY;
>>> > > >
>>> > > > - attribute_id =
>>> > > > - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>>> > > > - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>>> > > > - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
>>> > > > - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>>> > > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>>> > > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>>> > > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) |
>>> > > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>>> > > >
>>> > > > - ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
>>> > > > + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
>>> > > > if (ret)
>>> > > > return ret;
>>> > > >
>>> > > > @@ -803,7 +803,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>>> > > > if (value < capdata.min_value || value > capdata.max_value)
>>> > > > return -EINVAL;
>>> > > >
>>> > > > - args.arg0 = attribute_id;
>>> > > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>>> > > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>>> > > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) |
>>> > > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>>> > >
>>> > > It's already repeated a few times and you're adding more in this patch.
>>> > >
>>> > > We should have a helper function for this encoding as it seems to
>>> > > repeat. That is, something that takes tunable_attr and mode as input
>>> > > (the conversion of existing entries should be in own patch preceeding
>>> > > this fix patch).
>>> > >
>>> >
>>> > Hi Ilpo,
>>> >
>>> > A function for that is added in patch 10, though it is slightly modified from that to be more flexible is tunable_attr isn't used (such as with the fan test attributes)
>>> >
>>> > Originally I had that patch preceding any additions, but after
>>> > discussing with Rong we felt like it would be easier for stable
>>> > backports if all the fixes were upfront. I can certainly move it back
>>> > if you still prefer.
>>>
>>> Moving it back is OK for me, too.
>>>
>>> I think exposing non-fully-functioning fw-attrs on stable/LTS kernels
>>> should be acceptable as long as reading/writing these attributes doesn't
>>> break anything, which is exactly the case now (i.e., without this
>>> patch).
>>
>>How would refactoring the code into a helper result in changing stable
>>interface?
>>
>>Or did you perhaps move to talk about something entirely else (and I
>>ended up losing the context)?
>
>I meant the *current* LTS/stable state is acceptable for me as reading/writing these attributes doesn't break anything. Moving it back would be OK for me even if it made this patch unable to be backported (though unlikely, since moving it back makes the patch clearer as you've said). In other words, it doesn't matter for me whether the patch is backported or not.
>
>Some context: I didn't ask Derek to add Fixes: tag to this patch or move it. I asked him to rearrange other patches to make them backportable.
>
>Sorry for causing misunderstandings.
>
>Thanks,
>Rong
>
I preferred it earlier myself since the rest of the series was cleaner that way. I'll move it again as that's fairly simple. IMO stable should be able to take it as part of the fixes it affects since there are no functional changes and it will be a prerequisite for some much needed fixes on stable, but I suppose that's ultimately up to them.
I'll submit in a couple of days as I'm currently waiting on some feedback from Lenovo regarding some clarification of the charge limiting features after getting some unexpected results in a test.
Thanks,
Derek
>>
>>> Thanks a lot for your hard work in this series,
>>
>>
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
2026-05-05 17:20 ` Rong Zhang
2026-05-05 17:38 ` Derek J. Clark
@ 2026-05-05 18:42 ` Ilpo Järvinen
1 sibling, 0 replies; 17+ messages in thread
From: Ilpo Järvinen @ 2026-05-05 18:42 UTC (permalink / raw)
To: Rong Zhang
Cc: Derek J. Clark, Hans de Goede, Mark Pearson, Armin Wolf,
Jonathan Corbet, Kurt Borja, platform-driver-x86, LKML, stable
[-- Attachment #1: Type: text/plain, Size: 9388 bytes --]
On Wed, 6 May 2026, Rong Zhang wrote:
> Hi Ilpo,
>
> 于 2026年5月5日 GMT+08:00 18:25:34,"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> 写道:
> >On Fri, 1 May 2026, Rong Zhang wrote:
> >> On Thu, 2026-04-30 at 07:56 -0700, Derek J. Clark wrote:
> >> > On April 30, 2026 7:01:55 AM PDT, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> wrote:
> >> > > On Sun, 12 Apr 2026, Derek J. Clark wrote:
> >> > >
> >> > > > Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
> >> > > > if the attribute is supported by the hardware. Due to some poorly
> >> > > > implemented BIOS this is a multi-step sequence of events. This is
> >> > > > because:
> >> > > > - Some BIOS support getting the capability data from custom mode (0xff),
> >> > > > while others only support it in no-mode (0x00).
> >> > > > - Some BIOS support get/set for the current value from custom mode (0xff),
> >> > > > while others only support it in no-mode (0x00).
> >> > > > - Some BIOS report capability data for a method that is not fully
> >> > > > implemented.
> >> > > > - Some BIOS have methods fully implemented, but no complimentary
> >> > > > capability data.
> >> > > >
> >> > > > To ensure we only expose fully implemented methods with corresponding
> >> > > > capability data, we check each outcome before reporting that an
> >> > > > attribute can be supported.
> >> > > >
> >> > > > Checking for lwmi_is_attr_01_supported during remove is not done to
> >> > > > ensure that we don't attempt to call cd01 or send WMI events if one of
> >> > > > the interfaces being removed was the cause of the driver unloading.
> >> > > >
> >> > > > Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
> >> > > > Reported-by: Kurt Borja <kuurtb@gmail.com>
> >> > > > Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
> >> > > > Cc: stable@vger.kernel.org
> >> > > > Reviewed-by: Rong Zhang <i@rong.moe>
> >> > > > Tested-by: Rong Zhang <i@rong.moe>
> >> > > > Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> >> > > > Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> >> > > > ---
> >> > > > v7:
> >> > > > - Move earlier in the series. This required dropping the use of
> >> > > > lwmi_attr_id as it will be added later.
> >> > > > - Add missing switch between cd_mode_id and cv_mode_id in
> >> > > > current_value_store.
> >> > > > v6:
> >> > > > - Zero initialize args in lwmi_is_attr_01_supported.
> >> > > > - Fix formatting.
> >> > > > v5:
> >> > > > - Move cv/cd_mode_id refrences from path 3/4.
> >> > > > - Add missing import for ARRAY_SIZE.
> >> > > > - Make lwmi_is_attr_01_supported return bool instead of u32.
> >> > > > - Various formatting fixes.
> >> > > > v4:
> >> > > > - Use for loop instead of backtrace gotos for checking if an attribute
> >> > > > is supported.
> >> > > > - Add include for dev_printk.
> >> > > > - Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
> >> > > > - Don't use symmetric cleanup of attributes in error states.
> >> > > > ---
> >> > > > drivers/platform/x86/lenovo/wmi-gamezone.h | 1 +
> >> > > > drivers/platform/x86/lenovo/wmi-other.c | 114 ++++++++++++++++++---
> >> > > > 2 files changed, 98 insertions(+), 17 deletions(-)
> >> > > >
> >> > > > diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
> >> > > > index 6b163a5eeb95..ddb919cf6c36 100644
> >> > > > --- a/drivers/platform/x86/lenovo/wmi-gamezone.h
> >> > > > +++ b/drivers/platform/x86/lenovo/wmi-gamezone.h
> >> > > > @@ -10,6 +10,7 @@ enum gamezone_events_type {
> >> > > > };
> >> > > >
> >> > > > enum thermal_mode {
> >> > > > + LWMI_GZ_THERMAL_MODE_NONE = 0x00,
> >> > > > LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
> >> > > > LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
> >> > > > LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
> >> > > > diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> >> > > > index 50a03f5fd6ab..29d062a1c6dc 100644
> >> > > > --- a/drivers/platform/x86/lenovo/wmi-other.c
> >> > > > +++ b/drivers/platform/x86/lenovo/wmi-other.c
> >> > > > @@ -550,6 +550,8 @@ struct tunable_attr_01 {
> >> > > > u8 feature_id;
> >> > > > u8 device_id;
> >> > > > u8 type_id;
> >> > > > + u8 cd_mode_id; /* mode arg for searching capdata */
> >> > > > + u8 cv_mode_id; /* mode arg for set/get current_value */
> >> > > > };
> >> > > >
> >> > > > static struct tunable_attr_01 ppt_pl1_spl = {
> >> > > > @@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> >> > > > struct wmi_method_args_32 args = {};
> >> > > > struct capdata01 capdata;
> >> > > > enum thermal_mode mode;
> >> > > > - u32 attribute_id;
> >> > > > u32 value;
> >> > > > int ret;
> >> > > >
> >> > > > @@ -786,13 +787,12 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> >> > > > if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
> >> > > > return -EBUSY;
> >> > > >
> >> > > > - attribute_id =
> >> > > > - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> >> > > > - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> >> > > > - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
> >> > > > - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> >> > > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> >> > > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> >> > > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) |
> >> > > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> >> > > >
> >> > > > - ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
> >> > > > + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
> >> > > > if (ret)
> >> > > > return ret;
> >> > > >
> >> > > > @@ -803,7 +803,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> >> > > > if (value < capdata.min_value || value > capdata.max_value)
> >> > > > return -EINVAL;
> >> > > >
> >> > > > - args.arg0 = attribute_id;
> >> > > > + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> >> > > > + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> >> > > > + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) |
> >> > > > + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> >> > >
> >> > > It's already repeated a few times and you're adding more in this patch.
> >> > >
> >> > > We should have a helper function for this encoding as it seems to
> >> > > repeat. That is, something that takes tunable_attr and mode as input
> >> > > (the conversion of existing entries should be in own patch preceeding
> >> > > this fix patch).
> >> > >
> >> >
> >> > Hi Ilpo,
> >> >
> >> > A function for that is added in patch 10, though it is slightly modified from that to be more flexible is tunable_attr isn't used (such as with the fan test attributes)
> >> >
> >> > Originally I had that patch preceding any additions, but after
> >> > discussing with Rong we felt like it would be easier for stable
> >> > backports if all the fixes were upfront. I can certainly move it back
> >> > if you still prefer.
> >>
> >> Moving it back is OK for me, too.
> >>
> >> I think exposing non-fully-functioning fw-attrs on stable/LTS kernels
> >> should be acceptable as long as reading/writing these attributes doesn't
> >> break anything, which is exactly the case now (i.e., without this
> >> patch).
> >
> >How would refactoring the code into a helper result in changing stable
> >interface?
> >
> >Or did you perhaps move to talk about something entirely else (and I
> >ended up losing the context)?
>
> I meant the *current* LTS/stable state is acceptable for me as
> reading/writing these attributes doesn't break anything. Moving it back
> would be OK for me even if it made this patch unable to be backported
> (though unlikely, since moving it back makes the patch clearer as you've
> said). In other words, it doesn't matter for me whether the patch is
> backported or not.
Hi,
While I'm not a stable maintainer, I think they'd likely take the
non-fixes patch together with the fix.
In some cases, there's also benefit from having mainline and stable code
closer to each other which helps future backporting. And here the fix
patch actually becomes simpler if you do the cleanup first so each change
can be reviewed independently (its easier to trust correctness of both
because the fix change becomes simpler and a "no functional changes
intended" patches are typically much simpler to review that those with any
functional changes).
You can even ask stable people to pick up non-fixes by adding Cc:
stable to them.
(TBH, I personally feel that at times stable people do take too many
non-fixes, not the other way around.)
> Some context: I didn't ask Derek to add Fixes: tag to this patch or move
> it. I asked him to rearrange other patches to make them backportable.
>
> Sorry for causing misunderstandings.
It's no problem at all. :-)
--
i.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
2026-04-30 14:56 ` Derek J. Clark
2026-04-30 18:10 ` Rong Zhang
@ 2026-05-05 9:48 ` Ilpo Järvinen
2026-05-05 17:40 ` Derek J. Clark
1 sibling, 1 reply; 17+ messages in thread
From: Ilpo Järvinen @ 2026-05-05 9:48 UTC (permalink / raw)
To: Derek J. Clark
Cc: Hans de Goede, Mark Pearson, Armin Wolf, Jonathan Corbet,
Rong Zhang, Kurt Borja, platform-driver-x86, LKML, stable
[-- Attachment #1: Type: text/plain, Size: 6877 bytes --]
On Thu, 30 Apr 2026, Derek J. Clark wrote:
> On April 30, 2026 7:01:55 AM PDT, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> wrote:
> >On Sun, 12 Apr 2026, Derek J. Clark wrote:
> >
> >> Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
> >> if the attribute is supported by the hardware. Due to some poorly
> >> implemented BIOS this is a multi-step sequence of events. This is
> >> because:
> >> - Some BIOS support getting the capability data from custom mode (0xff),
> >> while others only support it in no-mode (0x00).
> >> - Some BIOS support get/set for the current value from custom mode (0xff),
> >> while others only support it in no-mode (0x00).
> >> - Some BIOS report capability data for a method that is not fully
> >> implemented.
> >> - Some BIOS have methods fully implemented, but no complimentary
> >> capability data.
> >>
> >> To ensure we only expose fully implemented methods with corresponding
> >> capability data, we check each outcome before reporting that an
> >> attribute can be supported.
> >>
> >> Checking for lwmi_is_attr_01_supported during remove is not done to
> >> ensure that we don't attempt to call cd01 or send WMI events if one of
> >> the interfaces being removed was the cause of the driver unloading.
> >>
> >> Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
> >> Reported-by: Kurt Borja <kuurtb@gmail.com>
> >> Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
> >> Cc: stable@vger.kernel.org
> >> Reviewed-by: Rong Zhang <i@rong.moe>
> >> Tested-by: Rong Zhang <i@rong.moe>
> >> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> >> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> >> ---
> >> v7:
> >> - Move earlier in the series. This required dropping the use of
> >> lwmi_attr_id as it will be added later.
> >> - Add missing switch between cd_mode_id and cv_mode_id in
> >> current_value_store.
> >> v6:
> >> - Zero initialize args in lwmi_is_attr_01_supported.
> >> - Fix formatting.
> >> v5:
> >> - Move cv/cd_mode_id refrences from path 3/4.
> >> - Add missing import for ARRAY_SIZE.
> >> - Make lwmi_is_attr_01_supported return bool instead of u32.
> >> - Various formatting fixes.
> >> v4:
> >> - Use for loop instead of backtrace gotos for checking if an attribute
> >> is supported.
> >> - Add include for dev_printk.
> >> - Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
> >> - Don't use symmetric cleanup of attributes in error states.
> >> ---
> >> drivers/platform/x86/lenovo/wmi-gamezone.h | 1 +
> >> drivers/platform/x86/lenovo/wmi-other.c | 114 ++++++++++++++++++---
> >> 2 files changed, 98 insertions(+), 17 deletions(-)
> >>
> >> diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
> >> index 6b163a5eeb95..ddb919cf6c36 100644
> >> --- a/drivers/platform/x86/lenovo/wmi-gamezone.h
> >> +++ b/drivers/platform/x86/lenovo/wmi-gamezone.h
> >> @@ -10,6 +10,7 @@ enum gamezone_events_type {
> >> };
> >>
> >> enum thermal_mode {
> >> + LWMI_GZ_THERMAL_MODE_NONE = 0x00,
> >> LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
> >> LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
> >> LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
> >> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> >> index 50a03f5fd6ab..29d062a1c6dc 100644
> >> --- a/drivers/platform/x86/lenovo/wmi-other.c
> >> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> >> @@ -550,6 +550,8 @@ struct tunable_attr_01 {
> >> u8 feature_id;
> >> u8 device_id;
> >> u8 type_id;
> >> + u8 cd_mode_id; /* mode arg for searching capdata */
> >> + u8 cv_mode_id; /* mode arg for set/get current_value */
> >> };
> >>
> >> static struct tunable_attr_01 ppt_pl1_spl = {
> >> @@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> >> struct wmi_method_args_32 args = {};
> >> struct capdata01 capdata;
> >> enum thermal_mode mode;
> >> - u32 attribute_id;
> >> u32 value;
> >> int ret;
> >>
> >> @@ -786,13 +787,12 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> >> if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
> >> return -EBUSY;
> >>
> >> - attribute_id =
> >> - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> >> - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> >> - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
> >> - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> >> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> >> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> >> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) |
> >> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> >>
> >> - ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
> >> + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
> >> if (ret)
> >> return ret;
> >>
> >> @@ -803,7 +803,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> >> if (value < capdata.min_value || value > capdata.max_value)
> >> return -EINVAL;
> >>
> >> - args.arg0 = attribute_id;
> >> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> >> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> >> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) |
> >> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> >
> >It's already repeated a few times and you're adding more in this patch.
> >
> >We should have a helper function for this encoding as it seems to
> >repeat. That is, something that takes tunable_attr and mode as input
> >(the conversion of existing entries should be in own patch preceeding
> >this fix patch).
> >
>
> Hi Ilpo,
>
> A function for that is added in patch 10, though it is slightly modified
> from that to be more flexible is tunable_attr isn't used (such as with
> the fan test attributes)
It still leaves some boilerplate having to deref all those tunable_attr
fields so perhaps it would be better to do nested helpers, one taking
tunable_attr and that calls the more flexible helper. IMO that would be
the best approach here.
> Originally I had that patch preceding any additions, but after
> discussing with Rong we felt like it would be easier for stable
> backports if all the fixes were upfront. I can certainly move it back
> if you still prefer.
I see. This patch would be cleaner though if we have the helper already in
place but I'm not insisting if you two prefer the current order of the
patches.
--
i.
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
2026-05-05 9:48 ` Ilpo Järvinen
@ 2026-05-05 17:40 ` Derek J. Clark
2026-05-05 18:44 ` Ilpo Järvinen
0 siblings, 1 reply; 17+ messages in thread
From: Derek J. Clark @ 2026-05-05 17:40 UTC (permalink / raw)
To: Ilpo Järvinen
Cc: Hans de Goede, Mark Pearson, Armin Wolf, Jonathan Corbet,
Rong Zhang, Kurt Borja, platform-driver-x86, LKML, stable
On May 5, 2026 2:48:08 AM PDT, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> wrote:
>On Thu, 30 Apr 2026, Derek J. Clark wrote:
>> On April 30, 2026 7:01:55 AM PDT, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> wrote:
>> >On Sun, 12 Apr 2026, Derek J. Clark wrote:
>> >
>> >> Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
>> >> if the attribute is supported by the hardware. Due to some poorly
>> >> implemented BIOS this is a multi-step sequence of events. This is
>> >> because:
>> >> - Some BIOS support getting the capability data from custom mode (0xff),
>> >> while others only support it in no-mode (0x00).
>> >> - Some BIOS support get/set for the current value from custom mode (0xff),
>> >> while others only support it in no-mode (0x00).
>> >> - Some BIOS report capability data for a method that is not fully
>> >> implemented.
>> >> - Some BIOS have methods fully implemented, but no complimentary
>> >> capability data.
>> >>
>> >> To ensure we only expose fully implemented methods with corresponding
>> >> capability data, we check each outcome before reporting that an
>> >> attribute can be supported.
>> >>
>> >> Checking for lwmi_is_attr_01_supported during remove is not done to
>> >> ensure that we don't attempt to call cd01 or send WMI events if one of
>> >> the interfaces being removed was the cause of the driver unloading.
>> >>
>> >> Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
>> >> Reported-by: Kurt Borja <kuurtb@gmail.com>
>> >> Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
>> >> Cc: stable@vger.kernel.org
>> >> Reviewed-by: Rong Zhang <i@rong.moe>
>> >> Tested-by: Rong Zhang <i@rong.moe>
>> >> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
>> >> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
>> >> ---
>> >> v7:
>> >> - Move earlier in the series. This required dropping the use of
>> >> lwmi_attr_id as it will be added later.
>> >> - Add missing switch between cd_mode_id and cv_mode_id in
>> >> current_value_store.
>> >> v6:
>> >> - Zero initialize args in lwmi_is_attr_01_supported.
>> >> - Fix formatting.
>> >> v5:
>> >> - Move cv/cd_mode_id refrences from path 3/4.
>> >> - Add missing import for ARRAY_SIZE.
>> >> - Make lwmi_is_attr_01_supported return bool instead of u32.
>> >> - Various formatting fixes.
>> >> v4:
>> >> - Use for loop instead of backtrace gotos for checking if an attribute
>> >> is supported.
>> >> - Add include for dev_printk.
>> >> - Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
>> >> - Don't use symmetric cleanup of attributes in error states.
>> >> ---
>> >> drivers/platform/x86/lenovo/wmi-gamezone.h | 1 +
>> >> drivers/platform/x86/lenovo/wmi-other.c | 114 ++++++++++++++++++---
>> >> 2 files changed, 98 insertions(+), 17 deletions(-)
>> >>
>> >> diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
>> >> index 6b163a5eeb95..ddb919cf6c36 100644
>> >> --- a/drivers/platform/x86/lenovo/wmi-gamezone.h
>> >> +++ b/drivers/platform/x86/lenovo/wmi-gamezone.h
>> >> @@ -10,6 +10,7 @@ enum gamezone_events_type {
>> >> };
>> >>
>> >> enum thermal_mode {
>> >> + LWMI_GZ_THERMAL_MODE_NONE = 0x00,
>> >> LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
>> >> LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
>> >> LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
>> >> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
>> >> index 50a03f5fd6ab..29d062a1c6dc 100644
>> >> --- a/drivers/platform/x86/lenovo/wmi-other.c
>> >> +++ b/drivers/platform/x86/lenovo/wmi-other.c
>> >> @@ -550,6 +550,8 @@ struct tunable_attr_01 {
>> >> u8 feature_id;
>> >> u8 device_id;
>> >> u8 type_id;
>> >> + u8 cd_mode_id; /* mode arg for searching capdata */
>> >> + u8 cv_mode_id; /* mode arg for set/get current_value */
>> >> };
>> >>
>> >> static struct tunable_attr_01 ppt_pl1_spl = {
>> >> @@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>> >> struct wmi_method_args_32 args = {};
>> >> struct capdata01 capdata;
>> >> enum thermal_mode mode;
>> >> - u32 attribute_id;
>> >> u32 value;
>> >> int ret;
>> >>
>> >> @@ -786,13 +787,12 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>> >> if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
>> >> return -EBUSY;
>> >>
>> >> - attribute_id =
>> >> - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> >> - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> >> - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
>> >> - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> >> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> >> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> >> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) |
>> >> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> >>
>> >> - ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
>> >> + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
>> >> if (ret)
>> >> return ret;
>> >>
>> >> @@ -803,7 +803,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>> >> if (value < capdata.min_value || value > capdata.max_value)
>> >> return -EINVAL;
>> >>
>> >> - args.arg0 = attribute_id;
>> >> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> >> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> >> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) |
>> >> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> >
>> >It's already repeated a few times and you're adding more in this patch.
>> >
>> >We should have a helper function for this encoding as it seems to
>> >repeat. That is, something that takes tunable_attr and mode as input
>> >(the conversion of existing entries should be in own patch preceeding
>> >this fix patch).
>> >
>>
>> Hi Ilpo,
>>
>> A function for that is added in patch 10, though it is slightly modified
>> from that to be more flexible is tunable_attr isn't used (such as with
>> the fan test attributes)
>
>It still leaves some boilerplate having to deref all those tunable_attr
>fields so perhaps it would be better to do nested helpers, one taking
>tunable_attr and that calls the more flexible helper. IMO that would be
>the best approach here.
>
That's simple enough. I assume that can be added to that same patch instead of an additional one?
- Derek
>> Originally I had that patch preceding any additions, but after
>> discussing with Rong we felt like it would be easier for stable
>> backports if all the fixes were upfront. I can certainly move it back
>> if you still prefer.
>
>I see. This patch would be cleaner though if we have the helper already in
>place but I'm not insisting if you two prefer the current order of the
>patches.
>
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
2026-05-05 17:40 ` Derek J. Clark
@ 2026-05-05 18:44 ` Ilpo Järvinen
0 siblings, 0 replies; 17+ messages in thread
From: Ilpo Järvinen @ 2026-05-05 18:44 UTC (permalink / raw)
To: Derek J. Clark
Cc: Hans de Goede, Mark Pearson, Armin Wolf, Jonathan Corbet,
Rong Zhang, Kurt Borja, platform-driver-x86, LKML, stable
[-- Attachment #1: Type: text/plain, Size: 7685 bytes --]
On Tue, 5 May 2026, Derek J. Clark wrote:
> On May 5, 2026 2:48:08 AM PDT, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> wrote:
> >On Thu, 30 Apr 2026, Derek J. Clark wrote:
> >> On April 30, 2026 7:01:55 AM PDT, "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com> wrote:
> >> >On Sun, 12 Apr 2026, Derek J. Clark wrote:
> >> >
> >> >> Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
> >> >> if the attribute is supported by the hardware. Due to some poorly
> >> >> implemented BIOS this is a multi-step sequence of events. This is
> >> >> because:
> >> >> - Some BIOS support getting the capability data from custom mode (0xff),
> >> >> while others only support it in no-mode (0x00).
> >> >> - Some BIOS support get/set for the current value from custom mode (0xff),
> >> >> while others only support it in no-mode (0x00).
> >> >> - Some BIOS report capability data for a method that is not fully
> >> >> implemented.
> >> >> - Some BIOS have methods fully implemented, but no complimentary
> >> >> capability data.
> >> >>
> >> >> To ensure we only expose fully implemented methods with corresponding
> >> >> capability data, we check each outcome before reporting that an
> >> >> attribute can be supported.
> >> >>
> >> >> Checking for lwmi_is_attr_01_supported during remove is not done to
> >> >> ensure that we don't attempt to call cd01 or send WMI events if one of
> >> >> the interfaces being removed was the cause of the driver unloading.
> >> >>
> >> >> Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
> >> >> Reported-by: Kurt Borja <kuurtb@gmail.com>
> >> >> Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
> >> >> Cc: stable@vger.kernel.org
> >> >> Reviewed-by: Rong Zhang <i@rong.moe>
> >> >> Tested-by: Rong Zhang <i@rong.moe>
> >> >> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> >> >> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> >> >> ---
> >> >> v7:
> >> >> - Move earlier in the series. This required dropping the use of
> >> >> lwmi_attr_id as it will be added later.
> >> >> - Add missing switch between cd_mode_id and cv_mode_id in
> >> >> current_value_store.
> >> >> v6:
> >> >> - Zero initialize args in lwmi_is_attr_01_supported.
> >> >> - Fix formatting.
> >> >> v5:
> >> >> - Move cv/cd_mode_id refrences from path 3/4.
> >> >> - Add missing import for ARRAY_SIZE.
> >> >> - Make lwmi_is_attr_01_supported return bool instead of u32.
> >> >> - Various formatting fixes.
> >> >> v4:
> >> >> - Use for loop instead of backtrace gotos for checking if an attribute
> >> >> is supported.
> >> >> - Add include for dev_printk.
> >> >> - Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
> >> >> - Don't use symmetric cleanup of attributes in error states.
> >> >> ---
> >> >> drivers/platform/x86/lenovo/wmi-gamezone.h | 1 +
> >> >> drivers/platform/x86/lenovo/wmi-other.c | 114 ++++++++++++++++++---
> >> >> 2 files changed, 98 insertions(+), 17 deletions(-)
> >> >>
> >> >> diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
> >> >> index 6b163a5eeb95..ddb919cf6c36 100644
> >> >> --- a/drivers/platform/x86/lenovo/wmi-gamezone.h
> >> >> +++ b/drivers/platform/x86/lenovo/wmi-gamezone.h
> >> >> @@ -10,6 +10,7 @@ enum gamezone_events_type {
> >> >> };
> >> >>
> >> >> enum thermal_mode {
> >> >> + LWMI_GZ_THERMAL_MODE_NONE = 0x00,
> >> >> LWMI_GZ_THERMAL_MODE_QUIET = 0x01,
> >> >> LWMI_GZ_THERMAL_MODE_BALANCED = 0x02,
> >> >> LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
> >> >> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> >> >> index 50a03f5fd6ab..29d062a1c6dc 100644
> >> >> --- a/drivers/platform/x86/lenovo/wmi-other.c
> >> >> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> >> >> @@ -550,6 +550,8 @@ struct tunable_attr_01 {
> >> >> u8 feature_id;
> >> >> u8 device_id;
> >> >> u8 type_id;
> >> >> + u8 cd_mode_id; /* mode arg for searching capdata */
> >> >> + u8 cv_mode_id; /* mode arg for set/get current_value */
> >> >> };
> >> >>
> >> >> static struct tunable_attr_01 ppt_pl1_spl = {
> >> >> @@ -775,7 +777,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> >> >> struct wmi_method_args_32 args = {};
> >> >> struct capdata01 capdata;
> >> >> enum thermal_mode mode;
> >> >> - u32 attribute_id;
> >> >> u32 value;
> >> >> int ret;
> >> >>
> >> >> @@ -786,13 +787,12 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> >> >> if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
> >> >> return -EBUSY;
> >> >>
> >> >> - attribute_id =
> >> >> - FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> >> >> - FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> >> >> - FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
> >> >> - FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> >> >> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> >> >> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> >> >> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cd_mode_id) |
> >> >> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> >> >>
> >> >> - ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
> >> >> + ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
> >> >> if (ret)
> >> >> return ret;
> >> >>
> >> >> @@ -803,7 +803,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
> >> >> if (value < capdata.min_value || value > capdata.max_value)
> >> >> return -EINVAL;
> >> >>
> >> >> - args.arg0 = attribute_id;
> >> >> + args.arg0 = FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> >> >> + FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> >> >> + FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, tunable_attr->cv_mode_id) |
> >> >> + FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> >> >
> >> >It's already repeated a few times and you're adding more in this patch.
> >> >
> >> >We should have a helper function for this encoding as it seems to
> >> >repeat. That is, something that takes tunable_attr and mode as input
> >> >(the conversion of existing entries should be in own patch preceeding
> >> >this fix patch).
> >> >
> >>
> >> Hi Ilpo,
> >>
> >> A function for that is added in patch 10, though it is slightly modified
> >> from that to be more flexible is tunable_attr isn't used (such as with
> >> the fan test attributes)
> >
> >It still leaves some boilerplate having to deref all those tunable_attr
> >fields so perhaps it would be better to do nested helpers, one taking
> >tunable_attr and that calls the more flexible helper. IMO that would be
> >the best approach here.
> >
>
> That's simple enough. I assume that can be added to that same patch
> instead of an additional one?
Yes, adding both of them in the same patch is fine. Otherwise you'd just
be changing the same spots again.
> >> Originally I had that patch preceding any additions, but after
> >> discussing with Rong we felt like it would be easier for stable
> >> backports if all the fixes were upfront. I can certainly move it back
> >> if you still prefer.
> >
> >I see. This patch would be cleaner though if we have the helper already in
> >place but I'm not insisting if you two prefer the current order of the
> >patches.
> >
>
--
i.
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v10 07/16] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other
[not found] <20260412211121.2220556-1-derekjohn.clark@gmail.com>
` (5 preceding siblings ...)
2026-04-12 21:11 ` [PATCH v10 06/16] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices Derek J. Clark
@ 2026-04-12 21:11 ` Derek J. Clark
6 siblings, 0 replies; 17+ messages in thread
From: Derek J. Clark @ 2026-04-12 21:11 UTC (permalink / raw)
To: Ilpo Järvinen, Hans de Goede
Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
Derek J . Clark, platform-driver-x86, linux-kernel, stable,
kernel test robot
From: Rong Zhang <i@rong.moe>
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.
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Fixes: 6e38b9fcbfa3 ("platform/x86: lenovo: gamezone needs "other mode"")
Cc: stable@vger.kernel.org
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202603252259.gHvJDyh3-lkp@intel.com/
Closes: https://lore.kernel.org/oe-kbuild-all/202603260302.X0NjQOda-lkp@intel.com/
Signed-off-by: Rong Zhang <i@rong.moe>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
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 f885127b007f..09b1b055d2e0 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 ca559e6c031d..a91089694727 100644
--- a/drivers/platform/x86/lenovo/wmi-gamezone.c
+++ b/drivers/platform/x86/lenovo/wmi-gamezone.c
@@ -23,7 +23,6 @@
#include "wmi-events.h"
#include "wmi-gamezone.h"
#include "wmi-helpers.h"
-#include "wmi-other.h"
#define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0"
@@ -385,7 +384,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[] = {
@@ -407,7 +406,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@gmail.com>");
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 018d7642e2bd..7a198259e393 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
+ *
+ * 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.
+ *
+ * Call lwmi_tm_unregister_notifier to unregister the notifier block from the
+ * thermal mode notifier chain.
+ *
+ * Return: 0 on success, %-ENOENT on error.
+ */
+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@gmail.com>");
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 20fd21749803..651a039228ed 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 {
@@ -17,4 +19,10 @@ struct wmi_method_args_32 {
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 29d062a1c6dc..4e88d6bee00c 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -40,7 +40,6 @@
#include <linux/kobject.h>
#include <linux/limits.h>
#include <linux/module.h>
-#include <linux/notifier.h>
#include <linux/platform_profile.h>
#include <linux/types.h>
#include <linux/wmi.h>
@@ -49,7 +48,6 @@
#include "wmi-events.h"
#include "wmi-gamezone.h"
#include "wmi-helpers.h"
-#include "wmi-other.h"
#include "../firmware_attributes_class.h"
#define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
@@ -81,7 +79,6 @@
#define LWMI_OM_FW_ATTR_BASE_PATH "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 {
@@ -109,7 +106,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;
@@ -577,102 +573,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 */
/**
@@ -780,7 +680,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;
@@ -843,7 +743,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@gmail.com> */
-
-#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_ */
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread