]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
thermal/core: Split __thermal_cooling_device_register() into two functions
authorDaniel Lezcano <daniel.lezcano@oss.qualcomm.com>
Tue, 5 May 2026 14:44:47 +0000 (16:44 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 15 May 2026 20:31:16 +0000 (22:31 +0200)
In preparation for the upcoming changes separating OF and non-OF code,
split __thermal_cooling_device_register() into allocation and addition
phases.

This allows moving the device node assignment out of the core
initialization path.

This change is not a trivial split. The lifetime of the cooling device
is managed by the device core through put_device(), which triggers
thermal_release() to free all associated resources.

With the introduction of thermal_cooling_device_alloc(), the allocation
path must mirror what thermal_release() undoes. In contrast,
thermal_cooling_device_add() must not perform any rollback and relies
on put_device() for cleanup on error paths. This avoids both double
free and resource leaks.

As part of this rework, add the missing device_initialize() call when
allocating the cooling device.

Suggested-by: Rafael J. Wysocki <rafael@kernel.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
[ rjw: Replace device_register() with device_add() ]
[ rjw: Rebase on top of previously applied material ]
Link: https://patch.msgid.link/20260505144447.2853933-1-daniel.lezcano@oss.qualcomm.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/thermal/thermal_core.c

index ca3b6bf2f292779eb5ba35ab1e37584ce11f2738..4e2a17fdb6a75fda2c30cb62be74ef7081850c8b 100644 (file)
@@ -963,29 +963,10 @@ static void thermal_cdev_release(struct device *dev)
        kfree(cdev);
 }
 
-/**
- * __thermal_cooling_device_register() - register a new thermal cooling device
- * @np:                a pointer to a device tree node.
- * @type:      the thermal cooling device type.
- * @devdata:   device private data.
- * @ops:       standard thermal cooling devices callbacks.
- *
- * This interface function adds a new thermal cooling device (fan/processor/...)
- * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
- * to all the thermal zone devices registered at the same time.
- * It also gives the opportunity to link the cooling device to a device tree
- * node, so that it can be bound to a thermal zone created out of device tree.
- *
- * Return: a pointer to the created struct thermal_cooling_device or an
- * ERR_PTR. Caller must check return value with IS_ERR*() helpers.
- */
 static struct thermal_cooling_device *
-__thermal_cooling_device_register(struct device_node *np,
-                                 const char *type, void *devdata,
-                                 const struct thermal_cooling_device_ops *ops)
+thermal_cooling_device_alloc(const char *type, const struct thermal_cooling_device_ops *ops)
 {
        struct thermal_cooling_device *cdev;
-       unsigned long current_state;
        int ret;
 
        if (!ops || !ops->get_max_state || !ops->get_cur_state ||
@@ -999,6 +980,8 @@ __thermal_cooling_device_register(struct device_node *np,
        if (!cdev)
                return ERR_PTR(-ENOMEM);
 
+       cdev->ops = ops;
+
        ret = ida_alloc(&thermal_cdev_ida, GFP_KERNEL);
        if (ret < 0)
                goto out_kfree_cdev;
@@ -1010,18 +993,37 @@ __thermal_cooling_device_register(struct device_node *np,
                goto out_ida_remove;
        }
 
+       return cdev;
+
+out_ida_remove:
+       ida_free(&thermal_cdev_ida, cdev->id);
+out_kfree_cdev:
+       kfree(cdev);
+       return ERR_PTR(ret);
+}
+
+static int thermal_cooling_device_add(struct thermal_cooling_device *cdev, void *devdata)
+{
+       unsigned long current_state;
+       int ret;
+
        mutex_init(&cdev->lock);
        INIT_LIST_HEAD(&cdev->thermal_instances);
-       cdev->np = np;
-       cdev->ops = ops;
        cdev->updated = false;
        cdev->device.class = thermal_class;
        cdev->device.release = thermal_cdev_release;
+       device_initialize(&cdev->device);
        cdev->devdata = devdata;
 
+       thermal_cooling_device_setup_sysfs(cdev);
+
+       ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
+       if (ret)
+               goto out_put_device;
+
        ret = cdev->ops->get_max_state(cdev, &cdev->max_state);
        if (ret)
-               goto out_cdev_type;
+               goto out_put_device;
 
        /*
         * The cooling device's current state is only needed for debug
@@ -1035,35 +1037,62 @@ __thermal_cooling_device_register(struct device_node *np,
        if (ret)
                current_state = ULONG_MAX;
 
-       thermal_cooling_device_setup_sysfs(cdev);
-
-       ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
+       ret = device_add(&cdev->device);
        if (ret)
-               goto out_cooling_dev;
-
-       ret = device_register(&cdev->device);
-       if (ret) {
-               /* thermal_release() handles rest of the cleanup */
-               put_device(&cdev->device);
-               return ERR_PTR(ret);
-       }
+               goto out_put_device;
 
        if (current_state <= cdev->max_state)
                thermal_debug_cdev_add(cdev, current_state);
 
        thermal_cooling_device_init_complete(cdev);
 
-       return cdev;
+       return 0;
 
-out_cooling_dev:
-       thermal_cooling_device_destroy_sysfs(cdev);
-out_cdev_type:
-       kfree_const(cdev->type);
-out_ida_remove:
-       ida_free(&thermal_cdev_ida, cdev->id);
-out_kfree_cdev:
-       kfree(cdev);
-       return ERR_PTR(ret);
+out_put_device:
+       /*
+        * The device core will release the memory via
+        * thermal_release() after put_device() is called in the error
+        * path
+        */
+       put_device(&cdev->device);
+       return ret;
+}
+
+/**
+ * __thermal_cooling_device_register() - register a new thermal cooling device
+ * @np:                a pointer to a device tree node.
+ * @type:      the thermal cooling device type.
+ * @devdata:   device private data.
+ * @ops:       standard thermal cooling devices callbacks.
+ *
+ * This interface function adds a new thermal cooling device (fan/processor/...)
+ * to /sys/class/thermal/ folder as cooling_device[0-*]. It tries to bind itself
+ * to all the thermal zone devices registered at the same time.
+ * It also gives the opportunity to link the cooling device to a device tree
+ * node, so that it can be bound to a thermal zone created out of device tree.
+ *
+ * Return: a pointer to the created struct thermal_cooling_device or an
+ * ERR_PTR. Caller must check return value with IS_ERR*() helpers.
+ */
+static struct thermal_cooling_device *
+__thermal_cooling_device_register(struct device_node *np,
+                                 const char *type, void *devdata,
+                                 const struct thermal_cooling_device_ops *ops)
+{
+       struct thermal_cooling_device *cdev;
+       int ret;
+
+       cdev = thermal_cooling_device_alloc(type, ops);
+       if (IS_ERR(cdev))
+               return cdev;
+
+       cdev->np = np;
+
+       ret = thermal_cooling_device_add(cdev, devdata);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return cdev;
 }
 
 /**