* [PATCH 1/4] thermal/core: Add dedicated release callback for cooling devices
2026-05-08 18:05 [PATCH 0/4] thermal/core: Decouple release paths for tz and cdev Daniel Lezcano
@ 2026-05-08 18:05 ` Daniel Lezcano
2026-05-08 18:05 ` [PATCH 2/4] thermal/core: Add dedicated release callback for thermal zones Daniel Lezcano
` (3 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Daniel Lezcano @ 2026-05-08 18:05 UTC (permalink / raw)
To: daniel.lezcano, lukasz.luba, rafael; +Cc: rui.zhang, linux-pm, linux-kernel
The thermal class release callback currently handles both thermal
zones and cooling devices by checking the device name prefix.
Move the cooling device cleanup to a dedicated struct device release
callback. This avoids relying on device names to select the release
path and keeps the cooling device lifetime handling local to the
cooling device object.
Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
---
drivers/thermal/thermal_core.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 2f4e2dc46b8f..cf5d4a9c11fe 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -952,7 +952,6 @@ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz,
static void thermal_release(struct device *dev)
{
struct thermal_zone_device *tz;
- struct thermal_cooling_device *cdev;
if (!strncmp(dev_name(dev), "thermal_zone",
sizeof("thermal_zone") - 1)) {
@@ -962,13 +961,6 @@ static void thermal_release(struct device *dev)
ida_destroy(&tz->ida);
mutex_destroy(&tz->lock);
complete(&tz->removal);
- } else if (!strncmp(dev_name(dev), "cooling_device",
- sizeof("cooling_device") - 1)) {
- cdev = to_cooling_device(dev);
- thermal_cooling_device_destroy_sysfs(cdev);
- kfree_const(cdev->type);
- ida_free(&thermal_cdev_ida, cdev->id);
- kfree(cdev);
}
}
@@ -1040,6 +1032,16 @@ static void thermal_cooling_device_init_complete(struct thermal_cooling_device *
thermal_zone_cdev_bind(tz, cdev);
}
+static void thermal_cdev_release(struct device *dev)
+{
+ struct thermal_cooling_device *cdev = to_cooling_device(dev);
+
+ thermal_cooling_device_destroy_sysfs(cdev);
+ kfree_const(cdev->type);
+ ida_free(&thermal_cdev_ida, cdev->id);
+ kfree(cdev);
+}
+
/**
* __thermal_cooling_device_register() - register a new thermal cooling device
* @np: a pointer to a device tree node.
@@ -1093,6 +1095,7 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->ops = ops;
cdev->updated = false;
cdev->device.class = &thermal_class;
+ cdev->device.release = thermal_cdev_release;
cdev->devdata = devdata;
ret = cdev->ops->get_max_state(cdev, &cdev->max_state);
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 2/4] thermal/core: Add dedicated release callback for thermal zones
2026-05-08 18:05 [PATCH 0/4] thermal/core: Decouple release paths for tz and cdev Daniel Lezcano
2026-05-08 18:05 ` [PATCH 1/4] thermal/core: Add dedicated release callback for cooling devices Daniel Lezcano
@ 2026-05-08 18:05 ` Daniel Lezcano
2026-05-08 18:05 ` [PATCH 3/4] thermal/core: Allocate the thermal class dynamically Daniel Lezcano
` (2 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Daniel Lezcano @ 2026-05-08 18:05 UTC (permalink / raw)
To: daniel.lezcano, lukasz.luba, rafael; +Cc: rui.zhang, linux-pm, linux-kernel
The thermal class release callback currently handles thermal zone
cleanup by checking the device name prefix.
Move the thermal zone cleanup to a dedicated struct device release
callback. This avoids relying on device names to select the release
path and keeps the thermal zone lifetime handling local to the thermal
zone object.
Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
---
drivers/thermal/thermal_core.c | 28 ++++++++++++----------------
1 file changed, 12 insertions(+), 16 deletions(-)
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index cf5d4a9c11fe..a79fc4cdb078 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -949,24 +949,8 @@ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz,
kfree(pos);
}
-static void thermal_release(struct device *dev)
-{
- struct thermal_zone_device *tz;
-
- if (!strncmp(dev_name(dev), "thermal_zone",
- sizeof("thermal_zone") - 1)) {
- tz = to_thermal_zone(dev);
- thermal_zone_destroy_device_groups(tz);
- thermal_set_governor(tz, NULL);
- ida_destroy(&tz->ida);
- mutex_destroy(&tz->lock);
- complete(&tz->removal);
- }
-}
-
static const struct class thermal_class = {
.name = "thermal",
- .dev_release = thermal_release,
};
static bool thermal_class_unavailable __ro_after_init = true;
@@ -1473,6 +1457,17 @@ static void thermal_zone_init_complete(struct thermal_zone_device *tz)
__thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
}
+static void thermal_zone_device_release(struct device *dev)
+{
+ struct thermal_zone_device *tz = to_thermal_zone(dev);
+
+ thermal_zone_destroy_device_groups(tz);
+ thermal_set_governor(tz, NULL);
+ ida_destroy(&tz->ida);
+ mutex_destroy(&tz->lock);
+ complete(&tz->removal);
+}
+
/**
* thermal_zone_device_register_with_trips() - register a new thermal zone device
* @type: the thermal zone device type
@@ -1580,6 +1575,7 @@ thermal_zone_device_register_with_trips(const char *type,
tz->ops.critical = thermal_zone_device_critical;
tz->device.class = &thermal_class;
+ tz->device.release = thermal_zone_device_release;
tz->devdata = devdata;
tz->num_trips = num_trips;
for_each_trip_desc(tz, td) {
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 3/4] thermal/core: Allocate the thermal class dynamically
2026-05-08 18:05 [PATCH 0/4] thermal/core: Decouple release paths for tz and cdev Daniel Lezcano
2026-05-08 18:05 ` [PATCH 1/4] thermal/core: Add dedicated release callback for cooling devices Daniel Lezcano
2026-05-08 18:05 ` [PATCH 2/4] thermal/core: Add dedicated release callback for thermal zones Daniel Lezcano
@ 2026-05-08 18:05 ` Daniel Lezcano
2026-05-08 18:24 ` Rafael J. Wysocki
2026-05-08 18:05 ` [PATCH 4/4] thermal/core: Use the thermal class pointer as init guard Daniel Lezcano
2026-05-08 18:10 ` [PATCH 0/4] thermal/core: Decouple release paths for tz and cdev Daniel Lezcano
4 siblings, 1 reply; 10+ messages in thread
From: Daniel Lezcano @ 2026-05-08 18:05 UTC (permalink / raw)
To: daniel.lezcano, lukasz.luba, rafael; +Cc: rui.zhang, linux-pm, linux-kernel
Use class_create() instead of a statically allocated struct class.
This allows the thermal class to be managed through a dynamically
allocated class object and avoids keeping a static class instance
around.
Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
---
drivers/thermal/thermal_core.c | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index a79fc4cdb078..748ab76823a3 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -949,9 +949,7 @@ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz,
kfree(pos);
}
-static const struct class thermal_class = {
- .name = "thermal",
-};
+static struct class *thermal_class;
static bool thermal_class_unavailable __ro_after_init = true;
static inline
@@ -1078,7 +1076,7 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->np = np;
cdev->ops = ops;
cdev->updated = false;
- cdev->device.class = &thermal_class;
+ cdev->device.class = thermal_class;
cdev->device.release = thermal_cdev_release;
cdev->devdata = devdata;
@@ -1574,7 +1572,7 @@ thermal_zone_device_register_with_trips(const char *type,
if (!tz->ops.critical)
tz->ops.critical = thermal_zone_device_critical;
- tz->device.class = &thermal_class;
+ tz->device.class = thermal_class;
tz->device.release = thermal_zone_device_release;
tz->devdata = devdata;
tz->num_trips = num_trips;
@@ -1898,15 +1896,18 @@ static int __init thermal_init(void)
if (result)
goto destroy_workqueue;
- result = class_register(&thermal_class);
- if (result)
+ thermal_class = class_create("thermal");
+ if (IS_ERR(thermal_class)) {
+ result = PTR_ERR(thermal_class);
goto unregister_governors;
+ }
thermal_class_unavailable = false;
return 0;
unregister_governors:
+ thermal_class = NULL;
thermal_unregister_governors();
destroy_workqueue:
destroy_workqueue(thermal_wq);
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH 3/4] thermal/core: Allocate the thermal class dynamically
2026-05-08 18:05 ` [PATCH 3/4] thermal/core: Allocate the thermal class dynamically Daniel Lezcano
@ 2026-05-08 18:24 ` Rafael J. Wysocki
2026-05-08 19:22 ` Daniel Lezcano
0 siblings, 1 reply; 10+ messages in thread
From: Rafael J. Wysocki @ 2026-05-08 18:24 UTC (permalink / raw)
To: Daniel Lezcano; +Cc: lukasz.luba, rafael, rui.zhang, linux-pm, linux-kernel
On Fri, May 8, 2026 at 8:05 PM Daniel Lezcano
<daniel.lezcano@oss.qualcomm.com> wrote:
>
> Use class_create() instead of a statically allocated struct class.
>
> This allows the thermal class to be managed through a dynamically
> allocated class object and avoids keeping a static class instance
> around.
I changed it the other way around during the last cycle, so is it
really worth changing again?
> Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
> ---
> drivers/thermal/thermal_core.c | 15 ++++++++-------
> 1 file changed, 8 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
> index a79fc4cdb078..748ab76823a3 100644
> --- a/drivers/thermal/thermal_core.c
> +++ b/drivers/thermal/thermal_core.c
> @@ -949,9 +949,7 @@ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz,
> kfree(pos);
> }
>
> -static const struct class thermal_class = {
> - .name = "thermal",
> -};
> +static struct class *thermal_class;
> static bool thermal_class_unavailable __ro_after_init = true;
>
> static inline
> @@ -1078,7 +1076,7 @@ __thermal_cooling_device_register(struct device_node *np,
> cdev->np = np;
> cdev->ops = ops;
> cdev->updated = false;
> - cdev->device.class = &thermal_class;
> + cdev->device.class = thermal_class;
> cdev->device.release = thermal_cdev_release;
> cdev->devdata = devdata;
>
> @@ -1574,7 +1572,7 @@ thermal_zone_device_register_with_trips(const char *type,
> if (!tz->ops.critical)
> tz->ops.critical = thermal_zone_device_critical;
>
> - tz->device.class = &thermal_class;
> + tz->device.class = thermal_class;
> tz->device.release = thermal_zone_device_release;
> tz->devdata = devdata;
> tz->num_trips = num_trips;
> @@ -1898,15 +1896,18 @@ static int __init thermal_init(void)
> if (result)
> goto destroy_workqueue;
>
> - result = class_register(&thermal_class);
> - if (result)
> + thermal_class = class_create("thermal");
> + if (IS_ERR(thermal_class)) {
> + result = PTR_ERR(thermal_class);
> goto unregister_governors;
> + }
>
> thermal_class_unavailable = false;
>
> return 0;
>
> unregister_governors:
> + thermal_class = NULL;
> thermal_unregister_governors();
> destroy_workqueue:
> destroy_workqueue(thermal_wq);
> --
> 2.43.0
>
>
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH 3/4] thermal/core: Allocate the thermal class dynamically
2026-05-08 18:24 ` Rafael J. Wysocki
@ 2026-05-08 19:22 ` Daniel Lezcano
2026-05-08 19:26 ` Rafael J. Wysocki
0 siblings, 1 reply; 10+ messages in thread
From: Daniel Lezcano @ 2026-05-08 19:22 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: lukasz.luba, rui.zhang, linux-pm, linux-kernel
On 5/8/26 20:24, Rafael J. Wysocki wrote:
> On Fri, May 8, 2026 at 8:05 PM Daniel Lezcano
> <daniel.lezcano@oss.qualcomm.com> wrote:
>>
>> Use class_create() instead of a statically allocated struct class.
>>
>> This allows the thermal class to be managed through a dynamically
>> allocated class object and avoids keeping a static class instance
>> around.
>
> I changed it the other way around during the last cycle, so is it
> really worth changing again?
Yes, I think it is because it does not reintroduce this change:
- thermal_class = kzalloc_obj(*thermal_class);
- if (!thermal_class) {
- result = -ENOMEM;
+ result = class_register(&thermal_class);
+ if (result)
goto unregister_governors;
- }
- thermal_class->name = "thermal";
- thermal_class->dev_release = thermal_release;
-
- result = class_register(thermal_class);
- if (result) {
- kfree(thermal_class);
- thermal_class = NULL;
- goto unregister_governors;
- }
It is replaced by thermal_create("thermal") which allocates and
registers the class and we get rid of two global variables. But up to you.
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH 3/4] thermal/core: Allocate the thermal class dynamically
2026-05-08 19:22 ` Daniel Lezcano
@ 2026-05-08 19:26 ` Rafael J. Wysocki
2026-05-08 19:28 ` Daniel Lezcano
0 siblings, 1 reply; 10+ messages in thread
From: Rafael J. Wysocki @ 2026-05-08 19:26 UTC (permalink / raw)
To: Daniel Lezcano
Cc: Rafael J. Wysocki, lukasz.luba, rui.zhang, linux-pm, linux-kernel
On Fri, May 8, 2026 at 9:22 PM Daniel Lezcano
<daniel.lezcano@oss.qualcomm.com> wrote:
>
> On 5/8/26 20:24, Rafael J. Wysocki wrote:
> > On Fri, May 8, 2026 at 8:05 PM Daniel Lezcano
> > <daniel.lezcano@oss.qualcomm.com> wrote:
> >>
> >> Use class_create() instead of a statically allocated struct class.
> >>
> >> This allows the thermal class to be managed through a dynamically
> >> allocated class object and avoids keeping a static class instance
> >> around.
> >
> > I changed it the other way around during the last cycle, so is it
> > really worth changing again?
>
> Yes, I think it is because it does not reintroduce this change:
>
> - thermal_class = kzalloc_obj(*thermal_class);
> - if (!thermal_class) {
> - result = -ENOMEM;
> + result = class_register(&thermal_class);
> + if (result)
> goto unregister_governors;
> - }
>
> - thermal_class->name = "thermal";
> - thermal_class->dev_release = thermal_release;
> -
> - result = class_register(thermal_class);
> - if (result) {
> - kfree(thermal_class);
> - thermal_class = NULL;
> - goto unregister_governors;
> - }
>
> It is replaced by thermal_create("thermal") which allocates and
> registers the class and we get rid of two global variables. But up to you.
Fair enough.
I'll queue up these changes for 7.2 early next week.
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH 3/4] thermal/core: Allocate the thermal class dynamically
2026-05-08 19:26 ` Rafael J. Wysocki
@ 2026-05-08 19:28 ` Daniel Lezcano
0 siblings, 0 replies; 10+ messages in thread
From: Daniel Lezcano @ 2026-05-08 19:28 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: lukasz.luba, rui.zhang, linux-pm, linux-kernel
On 5/8/26 21:26, Rafael J. Wysocki wrote:
> On Fri, May 8, 2026 at 9:22 PM Daniel Lezcano
> <daniel.lezcano@oss.qualcomm.com> wrote:
>>
>> On 5/8/26 20:24, Rafael J. Wysocki wrote:
>>> On Fri, May 8, 2026 at 8:05 PM Daniel Lezcano
>>> <daniel.lezcano@oss.qualcomm.com> wrote:
>>>>
>>>> Use class_create() instead of a statically allocated struct class.
>>>>
>>>> This allows the thermal class to be managed through a dynamically
>>>> allocated class object and avoids keeping a static class instance
>>>> around.
>>>
>>> I changed it the other way around during the last cycle, so is it
>>> really worth changing again?
>>
>> Yes, I think it is because it does not reintroduce this change:
>>
>> - thermal_class = kzalloc_obj(*thermal_class);
>> - if (!thermal_class) {
>> - result = -ENOMEM;
>> + result = class_register(&thermal_class);
>> + if (result)
>> goto unregister_governors;
>> - }
>>
>> - thermal_class->name = "thermal";
>> - thermal_class->dev_release = thermal_release;
>> -
>> - result = class_register(thermal_class);
>> - if (result) {
>> - kfree(thermal_class);
>> - thermal_class = NULL;
>> - goto unregister_governors;
>> - }
>>
>> It is replaced by thermal_create("thermal") which allocates and
>> registers the class and we get rid of two global variables. But up to you.
>
> Fair enough.
>
> I'll queue up these changes for 7.2 early next week.
Thanks
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 4/4] thermal/core: Use the thermal class pointer as init guard
2026-05-08 18:05 [PATCH 0/4] thermal/core: Decouple release paths for tz and cdev Daniel Lezcano
` (2 preceding siblings ...)
2026-05-08 18:05 ` [PATCH 3/4] thermal/core: Allocate the thermal class dynamically Daniel Lezcano
@ 2026-05-08 18:05 ` Daniel Lezcano
2026-05-08 18:10 ` [PATCH 0/4] thermal/core: Decouple release paths for tz and cdev Daniel Lezcano
4 siblings, 0 replies; 10+ messages in thread
From: Daniel Lezcano @ 2026-05-08 18:05 UTC (permalink / raw)
To: daniel.lezcano, lukasz.luba, rafael; +Cc: rui.zhang, linux-pm, linux-kernel
The thermal class is now dynamically allocated and stored as a
pointer.
Use the thermal_class pointer itself to check whether the thermal
class has been created instead of keeping a separate
thermal_class_unavailable flag.
Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
---
drivers/thermal/thermal_core.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 748ab76823a3..81f6bbaded65 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -950,7 +950,6 @@ static void thermal_unbind_cdev_from_trip(struct thermal_zone_device *tz,
}
static struct class *thermal_class;
-static bool thermal_class_unavailable __ro_after_init = true;
static inline
void print_bind_err_msg(struct thermal_zone_device *tz,
@@ -1053,7 +1052,7 @@ __thermal_cooling_device_register(struct device_node *np,
!ops->set_cur_state)
return ERR_PTR(-EINVAL);
- if (thermal_class_unavailable)
+ if (!thermal_class)
return ERR_PTR(-ENODEV);
cdev = kzalloc_obj(*cdev);
@@ -1536,7 +1535,7 @@ thermal_zone_device_register_with_trips(const char *type,
if (polling_delay && passive_delay > polling_delay)
return ERR_PTR(-EINVAL);
- if (thermal_class_unavailable)
+ if (!thermal_class)
return ERR_PTR(-ENODEV);
tz = kzalloc_flex(*tz, trips, num_trips);
@@ -1834,7 +1833,7 @@ static void __thermal_pm_prepare(void)
void thermal_pm_prepare(void)
{
- if (thermal_class_unavailable)
+ if (!thermal_class)
return;
__thermal_pm_prepare();
@@ -1865,7 +1864,7 @@ void thermal_pm_complete(void)
{
struct thermal_zone_device *tz;
- if (thermal_class_unavailable)
+ if (!thermal_class)
return;
guard(mutex)(&thermal_list_lock);
@@ -1902,8 +1901,6 @@ static int __init thermal_init(void)
goto unregister_governors;
}
- thermal_class_unavailable = false;
-
return 0;
unregister_governors:
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH 0/4] thermal/core: Decouple release paths for tz and cdev
2026-05-08 18:05 [PATCH 0/4] thermal/core: Decouple release paths for tz and cdev Daniel Lezcano
` (3 preceding siblings ...)
2026-05-08 18:05 ` [PATCH 4/4] thermal/core: Use the thermal class pointer as init guard Daniel Lezcano
@ 2026-05-08 18:10 ` Daniel Lezcano
4 siblings, 0 replies; 10+ messages in thread
From: Daniel Lezcano @ 2026-05-08 18:10 UTC (permalink / raw)
To: lukasz.luba, rafael; +Cc: rui.zhang, linux-pm, linux-kernel
On 5/8/26 20:05, Daniel Lezcano wrote:
> This series cleans up the lifetime handling of thermal devices.
>
> Currently, the thermal class release callback handles both thermal
> zones and cooling devices by checking the device name prefix. This
> approach is fragile and mixes responsibilities across different object
> types.
>
> The first two patches introduce dedicated struct device release
> callbacks for thermal zones and cooling devices. This removes the need
> to rely on device names and keeps the lifetime management local to
> each object.
>
> The last two patches convert the thermal class to a dynamically
> allocated object and simplify the initialization logic by using the
> class pointer itself as a guard instead of a separate boolean flag.
>
> Overall, this results in a clearer separation of responsibilities and
> more robust lifetime management aligned with the device model.
>
> No functional change intended.
Unless I’m mistaken, the patch sent to split the cooling device
registration is correct with this cleanup.
^ permalink raw reply [flat|nested] 10+ messages in thread