summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Lezcano <daniel.lezcano@oss.qualcomm.com>2026-05-05 17:44:47 +0300
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2026-05-15 23:31:16 +0300
commit13f4e660a1268dbc9c3fcba7fe214868c7c45062 (patch)
tree50ec77c49644ffe8fac097c9c0bffd8711222ca4
parentcfb5dc0f60fbe95a10cb307cc6dae5c47f160abb (diff)
downloadlinux-13f4e660a1268dbc9c3fcba7fe214868c7c45062.tar.xz
thermal/core: Split __thermal_cooling_device_register() into two functions
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>
-rw-r--r--drivers/thermal/thermal_core.c117
1 files changed, 73 insertions, 44 deletions
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index ca3b6bf2f292..4e2a17fdb6a7 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -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;
}
/**