Linux Hardware Monitor development
 help / color / mirror / Atom feed
* [PATCH v1 0/2] thermal: hwmon: Rework of automatic hwmon device registration
@ 2026-04-22 18:53 Rafael J. Wysocki
  2026-04-22 18:54 ` [PATCH v1 1/2] thermal: hwmon: Register one hwmon device for each thermal zone Rafael J. Wysocki
  2026-04-22 19:09 ` [PATCH v1 2/2] thermal: hwmon: Use extra_groups for adding temperature attributes Rafael J. Wysocki
  0 siblings, 2 replies; 6+ messages in thread
From: Rafael J. Wysocki @ 2026-04-22 18:53 UTC (permalink / raw)
  To: Linux PM
  Cc: Daniel Lezcano, LKML, Lukasz Luba, Armin Wolf, Jiajia Liu,
	Marc Zyngier, Enric Balletbo i Serra, linux-hwmon, Guenter Roeck

Hi All,

The first patch in the series reworks the automatic registration of hwmon
devices for thermal zones so that one hwmon device is registered for each of
them.  This is done to address a thermal zone removal deadlock related to the
sharing of a hwmon device with other thermal zones of the same type (see the
changelog of patch [1/2] for details).

The second patch simplifies the thermal hwmon code further by using the
canonical mechanism for registering extra sysfs attributes of hwmon devices
instead of manually adding files to sysfs.

Since this is a quite noticeable change of behavior, the patches are targeted
at 7.2.

Thanks!




^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v1 1/2] thermal: hwmon: Register one hwmon device for each thermal zone
  2026-04-22 18:53 [PATCH v1 0/2] thermal: hwmon: Rework of automatic hwmon device registration Rafael J. Wysocki
@ 2026-04-22 18:54 ` Rafael J. Wysocki
  2026-04-22 22:07   ` sashiko-bot
  2026-04-22 19:09 ` [PATCH v1 2/2] thermal: hwmon: Use extra_groups for adding temperature attributes Rafael J. Wysocki
  1 sibling, 1 reply; 6+ messages in thread
From: Rafael J. Wysocki @ 2026-04-22 18:54 UTC (permalink / raw)
  To: Linux PM
  Cc: Daniel Lezcano, LKML, Lukasz Luba, Armin Wolf, Jiajia Liu,
	Marc Zyngier, Enric Balletbo i Serra, linux-hwmon, Guenter Roeck

From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

The current code creates one hwmon device per thermal zone type and that
device is registered under the first thermal zone of the given type.

That turns out to be problematic when the thermal zone holding the
hwmon device is removed.

For example, say that there are two ACPI thermal zones on a system

/sys/devices/virtual/thermal/thermal_zone0/
/sys/devices/virtual/thermal/thermal_zone1/

The current code registers a hwmon class device for thermal_zone0 only:

/sys/devices/virtual/thermal/thermal_zone0/hwmon0/

because the type is "acpitz" for both of them, but it adds a sysfs
attribute that belongs to thermal_zone1 under it:

/sys/devices/virtual/thermal/thermal_zone0/hwmon0/temp2_input

There is also

/sys/devices/virtual/thermal/thermal_zone0/hwmon0/temp1_input

which belongs to thermal_zone0.

When thermal_zone0 is removed, say because the ACPI thermal driver is
unbound from the underlying platform device, the removal code skips the
removal of hwmon0 because of the temp2_input attribute belonging to
thermal_zone1 which effectively prevents thermal_zone0 removal from
making progress.

To address this problem, rework the thermal hwmon code to register one
hwmon device for each thermal zone, but since user space utilities
produce confusing output in some cases when there are multiple hwmon
devices with the same name attribute value present under thermal zones
of the same type, append the thermal zone ID preceded by an underline
character to the name of the hwmon device registered for that thermal
zone.

Link: https://lore.kernel.org/linux-pm/20260402021828.16556-1-liujia6264@gmail.com/
Fixes: f6b6b52ef7a5 ("thermal_hwmon: Pass the originating device down to hwmon_device_register_with_info")
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---

The reason why I have decided to take this approach is because it allows
the code to be simplified subsequently by using regular extra_groups for
hwmon instead of adding device attributes directly (see the next patch)
and going back to the state before commit f6b6b52ef7a5 is seriously
unattractive.  However, if it breaks user space for people, there really
won't be much choice.

---
 drivers/thermal/thermal_hwmon.c |  144 +++++++++++-----------------------------
 1 file changed, 41 insertions(+), 103 deletions(-)

--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -19,19 +19,8 @@
 #include "thermal_hwmon.h"
 #include "thermal_core.h"
 
-/* hwmon sys I/F */
-/* thermal zone devices with the same type share one hwmon device */
-struct thermal_hwmon_device {
-	char type[THERMAL_NAME_LENGTH];
-	struct device *device;
-	int count;
-	struct list_head tz_list;
-	struct list_head node;
-};
-
 struct thermal_hwmon_attr {
 	struct device_attribute attr;
-	char name[16];
 };
 
 /* one temperature input for each thermal zone */
@@ -42,6 +31,15 @@ struct thermal_hwmon_temp {
 	struct thermal_hwmon_attr temp_crit;	/* hwmon sys attr */
 };
 
+/* hwmon sys I/F */
+/* thermal zone devices with the same type share one hwmon device */
+struct thermal_hwmon_device {
+	char type[THERMAL_NAME_LENGTH];
+	struct device *device;
+	struct list_head node;
+	struct thermal_hwmon_temp tz_temp;
+};
+
 static LIST_HEAD(thermal_hwmon_list);
 
 static DEFINE_MUTEX(thermal_hwmon_list_lock);
@@ -87,45 +85,6 @@ temp_crit_show(struct device *dev, struc
 	return sysfs_emit(buf, "%d\n", temperature);
 }
 
-
-static struct thermal_hwmon_device *
-thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz)
-{
-	struct thermal_hwmon_device *hwmon;
-	char type[THERMAL_NAME_LENGTH];
-
-	mutex_lock(&thermal_hwmon_list_lock);
-	list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
-		strscpy(type, tz->type);
-		strreplace(type, '-', '_');
-		if (!strcmp(hwmon->type, type)) {
-			mutex_unlock(&thermal_hwmon_list_lock);
-			return hwmon;
-		}
-	}
-	mutex_unlock(&thermal_hwmon_list_lock);
-
-	return NULL;
-}
-
-/* Find the temperature input matching a given thermal zone */
-static struct thermal_hwmon_temp *
-thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
-			  const struct thermal_zone_device *tz)
-{
-	struct thermal_hwmon_temp *temp;
-
-	mutex_lock(&thermal_hwmon_list_lock);
-	list_for_each_entry(temp, &hwmon->tz_list, hwmon_node)
-		if (temp->tz == tz) {
-			mutex_unlock(&thermal_hwmon_list_lock);
-			return temp;
-		}
-	mutex_unlock(&thermal_hwmon_list_lock);
-
-	return NULL;
-}
-
 static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz)
 {
 	int temp;
@@ -136,21 +95,17 @@ int thermal_add_hwmon_sysfs(struct therm
 {
 	struct thermal_hwmon_device *hwmon;
 	struct thermal_hwmon_temp *temp;
-	int new_hwmon_device = 1;
 	int result;
 
-	hwmon = thermal_hwmon_lookup_by_type(tz);
-	if (hwmon) {
-		new_hwmon_device = 0;
-		goto register_sys_interface;
-	}
-
 	hwmon = kzalloc_obj(*hwmon);
 	if (!hwmon)
 		return -ENOMEM;
 
-	INIT_LIST_HEAD(&hwmon->tz_list);
-	strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
+	/*
+	 * Append the thermal zone ID preceded by an underline character to the
+	 * type to disambiguate the sensors command output.
+	 */
+	scnprintf(hwmon->type, THERMAL_NAME_LENGTH, "%s_%d", tz->type, tz->id);
 	strreplace(hwmon->type, '-', '_');
 	hwmon->device = hwmon_device_register_for_thermal(&tz->device,
 							  hwmon->type, hwmon);
@@ -159,31 +114,20 @@ int thermal_add_hwmon_sysfs(struct therm
 		goto free_mem;
 	}
 
- register_sys_interface:
-	temp = kzalloc_obj(*temp);
-	if (!temp) {
-		result = -ENOMEM;
-		goto unregister_name;
-	}
+	temp = &hwmon->tz_temp;
 
 	temp->tz = tz;
-	hwmon->count++;
 
-	snprintf(temp->temp_input.name, sizeof(temp->temp_input.name),
-		 "temp%d_input", hwmon->count);
-	temp->temp_input.attr.attr.name = temp->temp_input.name;
+	temp->temp_input.attr.attr.name = "temp1_input";
 	temp->temp_input.attr.attr.mode = 0444;
 	temp->temp_input.attr.show = temp_input_show;
 	sysfs_attr_init(&temp->temp_input.attr.attr);
 	result = device_create_file(hwmon->device, &temp->temp_input.attr);
 	if (result)
-		goto free_temp_mem;
+		goto unregister_name;
 
 	if (thermal_zone_crit_temp_valid(tz)) {
-		snprintf(temp->temp_crit.name,
-				sizeof(temp->temp_crit.name),
-				"temp%d_crit", hwmon->count);
-		temp->temp_crit.attr.attr.name = temp->temp_crit.name;
+		temp->temp_crit.attr.attr.name = "temp1_crit";
 		temp->temp_crit.attr.attr.mode = 0444;
 		temp->temp_crit.attr.show = temp_crit_show;
 		sysfs_attr_init(&temp->temp_crit.attr.attr);
@@ -193,21 +137,17 @@ int thermal_add_hwmon_sysfs(struct therm
 			goto unregister_input;
 	}
 
-	mutex_lock(&thermal_hwmon_list_lock);
-	if (new_hwmon_device)
+	/* The list is needed for hwmon lookup during removal. */
+	scoped_guard(mutex, &thermal_hwmon_list_lock) {
 		list_add_tail(&hwmon->node, &thermal_hwmon_list);
-	list_add_tail(&temp->hwmon_node, &hwmon->tz_list);
-	mutex_unlock(&thermal_hwmon_list_lock);
+	}
 
 	return 0;
 
  unregister_input:
 	device_remove_file(hwmon->device, &temp->temp_input.attr);
- free_temp_mem:
-	kfree(temp);
  unregister_name:
-	if (new_hwmon_device)
-		hwmon_device_unregister(hwmon->device);
+	hwmon_device_unregister(hwmon->device);
  free_mem:
 	kfree(hwmon);
 
@@ -215,39 +155,37 @@ int thermal_add_hwmon_sysfs(struct therm
 }
 EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs);
 
+static struct thermal_hwmon_device *
+thermal_hwmon_lookup(const struct thermal_zone_device *tz)
+{
+	struct thermal_hwmon_device *hwmon;
+
+	list_for_each_entry(hwmon, &thermal_hwmon_list, node) {
+		if (hwmon->tz_temp.tz == tz)
+			return hwmon;
+	}
+	return NULL;
+}
+
 void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
 {
 	struct thermal_hwmon_device *hwmon;
 	struct thermal_hwmon_temp *temp;
 
-	hwmon = thermal_hwmon_lookup_by_type(tz);
-	if (unlikely(!hwmon)) {
-		/* Should never happen... */
-		dev_dbg(&tz->device, "hwmon device lookup failed!\n");
-		return;
-	}
+	scoped_guard(mutex, &thermal_hwmon_list_lock) {
+		hwmon = thermal_hwmon_lookup(tz);
+		if (!hwmon)
+			return;
 
-	temp = thermal_hwmon_lookup_temp(hwmon, tz);
-	if (unlikely(!temp)) {
-		/* Should never happen... */
-		dev_dbg(&tz->device, "temperature input lookup failed!\n");
-		return;
+		list_del(&hwmon->node);
 	}
 
+	temp = &hwmon->tz_temp;
+
 	device_remove_file(hwmon->device, &temp->temp_input.attr);
 	if (thermal_zone_crit_temp_valid(tz))
 		device_remove_file(hwmon->device, &temp->temp_crit.attr);
 
-	mutex_lock(&thermal_hwmon_list_lock);
-	list_del(&temp->hwmon_node);
-	kfree(temp);
-	if (!list_empty(&hwmon->tz_list)) {
-		mutex_unlock(&thermal_hwmon_list_lock);
-		return;
-	}
-	list_del(&hwmon->node);
-	mutex_unlock(&thermal_hwmon_list_lock);
-
 	hwmon_device_unregister(hwmon->device);
 	kfree(hwmon);
 }




^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v1 2/2] thermal: hwmon: Use extra_groups for adding temperature attributes
  2026-04-22 18:53 [PATCH v1 0/2] thermal: hwmon: Rework of automatic hwmon device registration Rafael J. Wysocki
  2026-04-22 18:54 ` [PATCH v1 1/2] thermal: hwmon: Register one hwmon device for each thermal zone Rafael J. Wysocki
@ 2026-04-22 19:09 ` Rafael J. Wysocki
  2026-04-22 22:28   ` sashiko-bot
  1 sibling, 1 reply; 6+ messages in thread
From: Rafael J. Wysocki @ 2026-04-22 19:09 UTC (permalink / raw)
  To: Linux PM
  Cc: Daniel Lezcano, LKML, Lukasz Luba, Armin Wolf, Jiajia Liu,
	Marc Zyngier, Enric Balletbo i Serra, linux-hwmon, Guenter Roeck

From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Instead of passing NULL as the last argument to __hwmon_device_register()
in hwmon_device_register_for_thermal() and then adding each temperature
sysfs attribute to the hwmon device via device_create_file(), redefine
hwmon_device_register_for_thermal() to take an extra_groups argument
that will be passed to __hwmon_device_register(), define an attribute
group with a proper .is_visible() callback for the temperature
attributes and a related attribute groups pointer, and pass the latter
to hwmon_device_register_for_thermal().

This causes the code to be way more straightforward and closer to what
the other users of the hwmon subsystem do.

No intentional functional impact.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/hwmon/hwmon.c           |    6 +-
 drivers/thermal/thermal_hwmon.c |  112 ++++++++++++++++------------------------
 include/linux/hwmon.h           |    3 -
 3 files changed, 51 insertions(+), 70 deletions(-)

--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -1081,6 +1081,7 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_
  * @dev: the parent device
  * @name: hwmon name attribute
  * @drvdata: driver data to attach to created device
+ * @extra_groups: pointer to list of additional non-standard attribute groups
  *
  * The use of this function is restricted. It is provided for legacy reasons
  * and must only be called from the thermal subsystem.
@@ -1092,12 +1093,13 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_
  */
 struct device *
 hwmon_device_register_for_thermal(struct device *dev, const char *name,
-				  void *drvdata)
+				  void *drvdata,
+				  const struct attribute_group **extra_groups)
 {
 	if (!name || !dev)
 		return ERR_PTR(-EINVAL);
 
-	return __hwmon_device_register(dev, name, drvdata, NULL, NULL);
+	return __hwmon_device_register(dev, name, drvdata, NULL, extra_groups);
 }
 EXPORT_SYMBOL_NS_GPL(hwmon_device_register_for_thermal, "HWMON_THERMAL");
 
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -19,16 +19,12 @@
 #include "thermal_hwmon.h"
 #include "thermal_core.h"
 
-struct thermal_hwmon_attr {
-	struct device_attribute attr;
-};
-
 /* one temperature input for each thermal zone */
 struct thermal_hwmon_temp {
 	struct list_head hwmon_node;
 	struct thermal_zone_device *tz;
-	struct thermal_hwmon_attr temp_input;	/* hwmon sys attr */
-	struct thermal_hwmon_attr temp_crit;	/* hwmon sys attr */
+	struct device_attribute temp_input;	/* hwmon sys attr */
+	struct device_attribute temp_crit;	/* hwmon sys attr */
 };
 
 /* hwmon sys I/F */
@@ -45,19 +41,14 @@ static LIST_HEAD(thermal_hwmon_list);
 static DEFINE_MUTEX(thermal_hwmon_list_lock);
 
 static ssize_t
-temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
+temp1_input_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
+	struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
+	struct thermal_zone_device *tz = hwmon->tz_temp.tz;
 	int temperature;
 	int ret;
-	struct thermal_hwmon_attr *hwmon_attr
-			= container_of(attr, struct thermal_hwmon_attr, attr);
-	struct thermal_hwmon_temp *temp
-			= container_of(hwmon_attr, struct thermal_hwmon_temp,
-				       temp_input);
-	struct thermal_zone_device *tz = temp->tz;
 
 	ret = thermal_zone_get_temp(tz, &temperature);
-
 	if (ret)
 		return ret;
 
@@ -65,14 +56,10 @@ temp_input_show(struct device *dev, stru
 }
 
 static ssize_t
-temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
+temp1_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
-	struct thermal_hwmon_attr *hwmon_attr
-			= container_of(attr, struct thermal_hwmon_attr, attr);
-	struct thermal_hwmon_temp *temp
-			= container_of(hwmon_attr, struct thermal_hwmon_temp,
-				       temp_crit);
-	struct thermal_zone_device *tz = temp->tz;
+	struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
+	struct thermal_zone_device *tz = hwmon->tz_temp.tz;
 	int temperature;
 	int ret;
 
@@ -85,22 +72,49 @@ temp_crit_show(struct device *dev, struc
 	return sysfs_emit(buf, "%d\n", temperature);
 }
 
-static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz)
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RO(temp1_crit);
+
+static struct attribute *thermal_hwmon_attrs[] = {
+	&dev_attr_temp1_input.attr,
+	&dev_attr_temp1_crit.attr,
+	NULL,
+};
+
+static umode_t thermal_hwmon_attr_is_visible(struct kobject *kobj,
+					     struct attribute *a, int n)
 {
-	int temp;
-	return tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &temp);
+	if (a == &dev_attr_temp1_input.attr)
+		return a->mode;
+
+	if (a == &dev_attr_temp1_crit.attr) {
+		struct thermal_hwmon_device *hwmon = dev_get_drvdata(kobj_to_dev(kobj));
+		struct thermal_zone_device *tz = hwmon->tz_temp.tz;
+		int dummy;
+
+		if (tz->ops.get_crit_temp && !tz->ops.get_crit_temp(tz, &dummy))
+			return a->mode;
+	}
+
+	return 0;
 }
 
+static const struct attribute_group thermal_hwmon_group = {
+	.attrs	= thermal_hwmon_attrs,
+	.is_visible = thermal_hwmon_attr_is_visible,
+};
+
+__ATTRIBUTE_GROUPS(thermal_hwmon);
+
 int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
 {
 	struct thermal_hwmon_device *hwmon;
-	struct thermal_hwmon_temp *temp;
-	int result;
 
 	hwmon = kzalloc_obj(*hwmon);
 	if (!hwmon)
 		return -ENOMEM;
 
+	hwmon->tz_temp.tz = tz;
 	/*
 	 * Append the thermal zone ID preceded by an underline character to the
 	 * type to disambiguate the sensors command output.
@@ -108,33 +122,13 @@ int thermal_add_hwmon_sysfs(struct therm
 	scnprintf(hwmon->type, THERMAL_NAME_LENGTH, "%s_%d", tz->type, tz->id);
 	strreplace(hwmon->type, '-', '_');
 	hwmon->device = hwmon_device_register_for_thermal(&tz->device,
-							  hwmon->type, hwmon);
+							  hwmon->type, hwmon,
+							  thermal_hwmon_groups);
 	if (IS_ERR(hwmon->device)) {
-		result = PTR_ERR(hwmon->device);
-		goto free_mem;
-	}
+		int result = PTR_ERR(hwmon->device);
 
-	temp = &hwmon->tz_temp;
-
-	temp->tz = tz;
-
-	temp->temp_input.attr.attr.name = "temp1_input";
-	temp->temp_input.attr.attr.mode = 0444;
-	temp->temp_input.attr.show = temp_input_show;
-	sysfs_attr_init(&temp->temp_input.attr.attr);
-	result = device_create_file(hwmon->device, &temp->temp_input.attr);
-	if (result)
-		goto unregister_name;
-
-	if (thermal_zone_crit_temp_valid(tz)) {
-		temp->temp_crit.attr.attr.name = "temp1_crit";
-		temp->temp_crit.attr.attr.mode = 0444;
-		temp->temp_crit.attr.show = temp_crit_show;
-		sysfs_attr_init(&temp->temp_crit.attr.attr);
-		result = device_create_file(hwmon->device,
-					    &temp->temp_crit.attr);
-		if (result)
-			goto unregister_input;
+		kfree(hwmon);
+		return result;
 	}
 
 	/* The list is needed for hwmon lookup during removal. */
@@ -143,15 +137,6 @@ int thermal_add_hwmon_sysfs(struct therm
 	}
 
 	return 0;
-
- unregister_input:
-	device_remove_file(hwmon->device, &temp->temp_input.attr);
- unregister_name:
-	hwmon_device_unregister(hwmon->device);
- free_mem:
-	kfree(hwmon);
-
-	return result;
 }
 EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs);
 
@@ -170,7 +155,6 @@ thermal_hwmon_lookup(const struct therma
 void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
 {
 	struct thermal_hwmon_device *hwmon;
-	struct thermal_hwmon_temp *temp;
 
 	scoped_guard(mutex, &thermal_hwmon_list_lock) {
 		hwmon = thermal_hwmon_lookup(tz);
@@ -180,12 +164,6 @@ void thermal_remove_hwmon_sysfs(struct t
 		list_del(&hwmon->node);
 	}
 
-	temp = &hwmon->tz_temp;
-
-	device_remove_file(hwmon->device, &temp->temp_input.attr);
-	if (thermal_zone_crit_temp_valid(tz))
-		device_remove_file(hwmon->device, &temp->temp_crit.attr);
-
 	hwmon_device_unregister(hwmon->device);
 	kfree(hwmon);
 }
--- a/include/linux/hwmon.h
+++ b/include/linux/hwmon.h
@@ -477,7 +477,8 @@ hwmon_device_register_with_info(struct d
 				const struct attribute_group **extra_groups);
 struct device *
 hwmon_device_register_for_thermal(struct device *dev, const char *name,
-				  void *drvdata);
+				  void *drvdata,
+				  const struct attribute_group **extra_groups);
 struct device *
 devm_hwmon_device_register_with_info(struct device *dev,
 				const char *name, void *drvdata,




^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v1 1/2] thermal: hwmon: Register one hwmon device for each thermal zone
  2026-04-22 18:54 ` [PATCH v1 1/2] thermal: hwmon: Register one hwmon device for each thermal zone Rafael J. Wysocki
@ 2026-04-22 22:07   ` sashiko-bot
  2026-04-30 19:41     ` Rafael J. Wysocki
  0 siblings, 1 reply; 6+ messages in thread
From: sashiko-bot @ 2026-04-22 22:07 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: linux-hwmon

Sashiko AI review found 5 potential issue(s):
- [Critical] UAPI Breakage by changing existing hwmon device names
- [High] Buffer truncation during hwmon name construction defeats disambiguation
- [High] Kernel panic during thermal zone removal due to conditionally re-evaluating hardware-dependent state
- [Medium] Subsystem Guideline Violation: Mixing goto and scoped_guard
- [Low] Unused dead code (hwmon_node) left in struct thermal_hwmon_temp
--

commit 7f75c80d7fc0d5195ae6453505545af64be540ea
Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Subject: thermal: hwmon: Register one hwmon device for each thermal zone

The commit reworks the thermal hwmon code to register one hwmon device for each
thermal zone. It appends the thermal zone ID to the hwmon device name to
disambiguate identical names.

Link: https://lore.kernel.org/linux-pm/20260402021828.16556-1-liujia6264@gmail.com/

> diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
> index b624892bc6d66..46c40c0852804 100644
> --- a/drivers/thermal/thermal_hwmon.c
> +++ b/drivers/thermal/thermal_hwmon.c
[ ... ]
> @@ -136,21 +95,17 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
[ ... ]
>  	hwmon = kzalloc_obj(*hwmon);
>  	if (!hwmon)
>  		return -ENOMEM;
>  
> -	INIT_LIST_HEAD(&hwmon->tz_list);
> -	strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
> +	/*
> +	 * Append the thermal zone ID preceded by an underline character to the
> +	 * type to disambiguate the sensors command output.
> +	 */
> +	scnprintf(hwmon->type, THERMAL_NAME_LENGTH, "%s_%d", tz->type, tz->id);

Could this truncation defeat the disambiguation? hwmon->type is defined as
char type[THERMAL_NAME_LENGTH]. If tz->type is already near
THERMAL_NAME_LENGTH characters long, the appended _%d suffix would be
truncated to fit the buffer, potentially resulting in identical names for
different zones.

Can appending a suffix to the name break the userspace ABI? Existing
user-space utilities and configurations that match exactly on the original
zone type string might no longer work when _0 is appended to the name.

>  	strreplace(hwmon->type, '-', '_');
>  	hwmon->device = hwmon_device_register_for_thermal(&tz->device,
>  							  hwmon->type, hwmon);
[ ... ]
> @@ -193,21 +137,17 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
>  			goto unregister_input;
>  	}
>  
> -	mutex_lock(&thermal_hwmon_list_lock);
> -	if (new_hwmon_device)
> +	/* The list is needed for hwmon lookup during removal. */
> +	scoped_guard(mutex, &thermal_hwmon_list_lock) {
>  		list_add_tail(&hwmon->node, &thermal_hwmon_list);
> -	list_add_tail(&temp->hwmon_node, &hwmon->tz_list);

Since hwmon->tz_list is removed, does temp->hwmon_node still need to exist
in struct thermal_hwmon_temp? It looks like it is no longer used anywhere.

> -	mutex_unlock(&thermal_hwmon_list_lock);
> +	}
>  
>  	return 0;
>  
>   unregister_input:
>  	device_remove_file(hwmon->device, &temp->temp_input.attr);
> - free_temp_mem:
> -	kfree(temp);
>   unregister_name:
> -	if (new_hwmon_device)
> -		hwmon_device_unregister(hwmon->device);
> +	hwmon_device_unregister(hwmon->device);
>   free_mem:
>  	kfree(hwmon);

Does this code mix goto-based error handling and scope-based cleanup helpers?
The cleanup subsystem guidelines expect that usage of goto and cleanup
helpers are never mixed in the same function.

This was noted as being fixed later in the series, but is included here
for completeness.

>  
> @@ -215,39 +155,37 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
[ ... ]
>  void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
>  {
>  	struct thermal_hwmon_device *hwmon;
>  	struct thermal_hwmon_temp *temp;
>  
> -	hwmon = thermal_hwmon_lookup_by_type(tz);
> -	if (unlikely(!hwmon)) {
> -		/* Should never happen... */
> -		dev_dbg(&tz->device, "hwmon device lookup failed!\n");
> -		return;
> -	}
> +	scoped_guard(mutex, &thermal_hwmon_list_lock) {
> +		hwmon = thermal_hwmon_lookup(tz);
> +		if (!hwmon)
> +			return;
>  
> -	temp = thermal_hwmon_lookup_temp(hwmon, tz);
> -	if (unlikely(!temp)) {
> -		/* Should never happen... */
> -		dev_dbg(&tz->device, "temperature input lookup failed!\n");
> -		return;
> +		list_del(&hwmon->node);
>  	}
>  
> +	temp = &hwmon->tz_temp;
> +
>  	device_remove_file(hwmon->device, &temp->temp_input.attr);
>  	if (thermal_zone_crit_temp_valid(tz))
>  		device_remove_file(hwmon->device, &temp->temp_crit.attr);

Is there a potential panic during removal if the hardware state changes?

If thermal_zone_crit_temp_valid() fails during thermal_add_hwmon_sysfs()
initialization, the temp_crit attribute is never created and its name
remains NULL.

If the hardware state later recovers and thermal_zone_crit_temp_valid()
returns true during thermal_remove_hwmon_sysfs(), device_remove_file()
will be called with a NULL name, which can cause a panic in the sysfs core.

This issue was also noted as being fixed later in the series.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/5100596.31r3eYUQgx@rafael.j.wysocki?part=1

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v1 2/2] thermal: hwmon: Use extra_groups for adding temperature attributes
  2026-04-22 19:09 ` [PATCH v1 2/2] thermal: hwmon: Use extra_groups for adding temperature attributes Rafael J. Wysocki
@ 2026-04-22 22:28   ` sashiko-bot
  0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-04-22 22:28 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: linux-hwmon

Sashiko AI review found 1 potential issue(s):
- [Low] Incomplete refactoring leaves unused fields in `struct thermal_hwmon_temp`.
--

commit 31b1d3d5cb622738ced37ad0ce85eba8a7a4b533
Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

thermal: hwmon: Use extra_groups for adding temperature attributes

This commit simplifies the thermal hwmon device registration by passing an
attribute group directly via the extra_groups argument instead of adding
each sysfs attribute individually.

> diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
> index 46c40c0852804..49d06cf27bd87 100644
> --- a/drivers/thermal/thermal_hwmon.c
> +++ b/drivers/thermal/thermal_hwmon.c

[ ... ]

>  /* one temperature input for each thermal zone */
>  struct thermal_hwmon_temp {
>  	struct list_head hwmon_node;
>  	struct thermal_zone_device *tz;
> -	struct thermal_hwmon_attr temp_input;	/* hwmon sys attr */
> -	struct thermal_hwmon_attr temp_crit;	/* hwmon sys attr */
> +	struct device_attribute temp_input;	/* hwmon sys attr */
> +	struct device_attribute temp_crit;	/* hwmon sys attr */
>  };

Are temp_input and temp_crit still needed in struct thermal_hwmon_temp?

Now that the attributes are registered via the static thermal_hwmon_group
using DEVICE_ATTR_RO, it looks like these fields are no longer initialized
or used anywhere in the updated code.

Could they be removed entirely to avoid keeping dead fields and to save memory
in the dynamically allocated struct thermal_hwmon_device?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/5100596.31r3eYUQgx@rafael.j.wysocki?part=2

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v1 1/2] thermal: hwmon: Register one hwmon device for each thermal zone
  2026-04-22 22:07   ` sashiko-bot
@ 2026-04-30 19:41     ` Rafael J. Wysocki
  0 siblings, 0 replies; 6+ messages in thread
From: Rafael J. Wysocki @ 2026-04-30 19:41 UTC (permalink / raw)
  To: sashiko; +Cc: Rafael J. Wysocki, linux-hwmon

On Thu, Apr 23, 2026 at 12:07 AM <sashiko-bot@kernel.org> wrote:
>
> Sashiko AI review found 5 potential issue(s):
> - [Critical] UAPI Breakage by changing existing hwmon device names

This is done on purpose as explained in the changelog and if it really
breaks user space for somebody, the entire approach will need to be
changed (to something much less attractive).

> - [High] Buffer truncation during hwmon name construction defeats disambiguation

That problem is unlikely to happen, but formally it is a genuine one.
It will be addressed in the v2 of the patch.

> - [High] Kernel panic during thermal zone removal due to conditionally re-evaluating hardware-dependent state

That issue exists in the current code.  I'll add a patch to address it in v2.

> - [Medium] Subsystem Guideline Violation: Mixing goto and scoped_guard

Good point, will fix it up in v2.

> - [Low] Unused dead code (hwmon_node) left in struct thermal_hwmon_temp

This will also be addressed in v2.

Thanks!

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-04-30 19:42 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-22 18:53 [PATCH v1 0/2] thermal: hwmon: Rework of automatic hwmon device registration Rafael J. Wysocki
2026-04-22 18:54 ` [PATCH v1 1/2] thermal: hwmon: Register one hwmon device for each thermal zone Rafael J. Wysocki
2026-04-22 22:07   ` sashiko-bot
2026-04-30 19:41     ` Rafael J. Wysocki
2026-04-22 19:09 ` [PATCH v1 2/2] thermal: hwmon: Use extra_groups for adding temperature attributes Rafael J. Wysocki
2026-04-22 22:28   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox