thermal: core: Make thermal_zone_device_unregister() return after freeing the zone
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 8 Dec 2023 19:13:44 +0000 (20:13 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 11 Dec 2023 19:49:53 +0000 (20:49 +0100)
Make thermal_zone_device_unregister() wait until all of the references
to the given thermal zone object have been dropped and free it before
returning.

This guarantees that when thermal_zone_device_unregister() returns,
there is no leftover activity regarding the thermal zone in question
which is required by some of its callers (for instance, modular driver
code that wants to know when it is safe to let the module go away).

Subsequently, this will allow some confusing device_is_registered()
checks to be dropped from the thermal sysfs and core code.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-and-tested-by: Lukasz Luba <lukasz.luba@arm.com>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
drivers/thermal/thermal_core.c
include/linux/thermal.h

index 625ba07cbe2f0d781d9d5f1e330cfb8663553d9c..70a294d121872500b4c6bbe26830e0baf35b1b7a 100644 (file)
@@ -822,7 +822,7 @@ static void thermal_release(struct device *dev)
                tz = to_thermal_zone(dev);
                thermal_zone_destroy_device_groups(tz);
                mutex_destroy(&tz->lock);
-               kfree(tz);
+               complete(&tz->removal);
        } else if (!strncmp(dev_name(dev), "cooling_device",
                            sizeof("cooling_device") - 1)) {
                cdev = to_cooling_device(dev);
@@ -1315,6 +1315,7 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t
        INIT_LIST_HEAD(&tz->thermal_instances);
        ida_init(&tz->ida);
        mutex_init(&tz->lock);
+       init_completion(&tz->removal);
        id = ida_alloc(&thermal_tz_ida, GFP_KERNEL);
        if (id < 0) {
                result = id;
@@ -1494,6 +1495,9 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
        put_device(&tz->device);
 
        thermal_notify_tz_delete(tz_id);
+
+       wait_for_completion(&tz->removal);
+       kfree(tz);
 }
 EXPORT_SYMBOL_GPL(thermal_zone_device_unregister);
 
index 0ea99f50d57c5b0565ed6c31b10b9dccd917c22b..bedbaec9a42e1689784eaf98c8964016ac0ae929 100644 (file)
@@ -117,6 +117,7 @@ struct thermal_cooling_device {
  * @id:                unique id number for each thermal zone
  * @type:      the thermal zone device type
  * @device:    &struct device for this thermal zone
+ * @removal:   removal completion
  * @trip_temp_attrs:   attributes for trip points for sysfs: trip temperature
  * @trip_type_attrs:   attributes for trip points for sysfs: trip type
  * @trip_hyst_attrs:   attributes for trip points for sysfs: trip hysteresis
@@ -156,6 +157,7 @@ struct thermal_zone_device {
        int id;
        char type[THERMAL_NAME_LENGTH];
        struct device device;
+       struct completion removal;
        struct attribute_group trips_attribute_group;
        struct thermal_attr *trip_temp_attrs;
        struct thermal_attr *trip_type_attrs;