@@ -31,8 +31,6 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
trip, trip_temp, tz->temperature,
trip_hyst);
- mutex_lock(&tz->lock);
-
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->trip != trip)
continue;
@@ -65,8 +63,6 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
instance->cdev->updated = false; /* cdev needs update */
mutex_unlock(&instance->cdev->lock);
}
-
- mutex_unlock(&tz->lock);
}
/**
@@ -102,13 +98,9 @@ static int bang_bang_control(struct thermal_zone_device *tz, int trip)
thermal_zone_trip_update(tz, trip);
- mutex_lock(&tz->lock);
-
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
thermal_cdev_update(instance->cdev);
- mutex_unlock(&tz->lock);
-
return 0;
}
@@ -82,8 +82,6 @@ static int fair_share_throttle(struct thermal_zone_device *tz, int trip)
int total_instance = 0;
int cur_trip_level = get_trip_level(tz);
- mutex_lock(&tz->lock);
-
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->trip != trip)
continue;
@@ -112,7 +110,6 @@ static int fair_share_throttle(struct thermal_zone_device *tz, int trip)
mutex_unlock(&cdev->lock);
}
- mutex_unlock(&tz->lock);
return 0;
}
@@ -392,8 +392,6 @@ static int allocate_power(struct thermal_zone_device *tz,
int i, num_actors, total_weight, ret = 0;
int trip_max_desired_temperature = params->trip_max_desired_temperature;
- mutex_lock(&tz->lock);
-
num_actors = 0;
total_weight = 0;
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
@@ -404,10 +402,8 @@ static int allocate_power(struct thermal_zone_device *tz,
}
}
- if (!num_actors) {
- ret = -ENODEV;
- goto unlock;
- }
+ if (!num_actors)
+ return -ENODEV;
/*
* We need to allocate five arrays of the same size:
@@ -421,10 +417,8 @@ static int allocate_power(struct thermal_zone_device *tz,
BUILD_BUG_ON(sizeof(*req_power) != sizeof(*extra_actor_power));
BUILD_BUG_ON(sizeof(*req_power) != sizeof(*weighted_req_power));
req_power = kcalloc(num_actors * 5, sizeof(*req_power), GFP_KERNEL);
- if (!req_power) {
- ret = -ENOMEM;
- goto unlock;
- }
+ if (!req_power)
+ return -ENOMEM;
max_power = &req_power[num_actors];
granted_power = &req_power[2 * num_actors];
@@ -496,9 +490,6 @@ static int allocate_power(struct thermal_zone_device *tz,
control_temp - tz->temperature);
kfree(req_power);
-unlock:
- mutex_unlock(&tz->lock);
-
return ret;
}
@@ -576,7 +567,6 @@ static void allow_maximum_power(struct thermal_zone_device *tz, bool update)
struct power_allocator_params *params = tz->governor_data;
u32 req_power;
- mutex_lock(&tz->lock);
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
struct thermal_cooling_device *cdev = instance->cdev;
@@ -598,7 +588,6 @@ static void allow_maximum_power(struct thermal_zone_device *tz, bool update)
mutex_unlock(&instance->cdev->lock);
}
- mutex_unlock(&tz->lock);
}
/**
@@ -117,8 +117,6 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
trip, trip_type, trip_temp, trend, throttle);
- mutex_lock(&tz->lock);
-
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->trip != trip)
continue;
@@ -145,8 +143,6 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
instance->cdev->updated = false; /* cdev needs update */
mutex_unlock(&instance->cdev->lock);
}
-
- mutex_unlock(&tz->lock);
}
/**
@@ -166,13 +162,9 @@ static int step_wise_throttle(struct thermal_zone_device *tz, int trip)
thermal_zone_trip_update(tz, trip);
- mutex_lock(&tz->lock);
-
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
thermal_cdev_update(instance->cdev);
- mutex_unlock(&tz->lock);
-
return 0;
}
@@ -34,7 +34,6 @@ static int notify_user_space(struct thermal_zone_device *tz, int trip)
char *thermal_prop[5];
int i;
- mutex_lock(&tz->lock);
thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", tz->type);
thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", tz->temperature);
thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=%d", trip);
@@ -43,7 +42,6 @@ static int notify_user_space(struct thermal_zone_device *tz, int trip)
kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, thermal_prop);
for (i = 0; i < 4; ++i)
kfree(thermal_prop[i]);
- mutex_unlock(&tz->lock);
return 0;
}
@@ -295,9 +295,14 @@ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
cancel_delayed_work(&tz->poll_queue);
}
+static int __thermal_zone_device_is_enabled(struct thermal_zone_device *tz)
+{
+ return tz->ops && tz->mode == THERMAL_DEVICE_ENABLED;
+}
+
static inline bool should_stop_polling(struct thermal_zone_device *tz)
{
- return !thermal_zone_device_is_enabled(tz);
+ return !__thermal_zone_device_is_enabled(tz);
}
static void monitor_thermal_zone(struct thermal_zone_device *tz)
@@ -306,16 +311,12 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz)
stop = should_stop_polling(tz);
- mutex_lock(&tz->lock);
-
if (!stop && tz->passive)
thermal_zone_device_set_polling(tz, tz->passive_delay_jiffies);
else if (!stop && tz->polling_delay_jiffies)
thermal_zone_device_set_polling(tz, tz->polling_delay_jiffies);
else
thermal_zone_device_set_polling(tz, 0);
-
- mutex_unlock(&tz->lock);
}
static void handle_non_critical_trips(struct thermal_zone_device *tz, int trip)
@@ -394,7 +395,7 @@ static void update_temperature(struct thermal_zone_device *tz)
{
int temp, ret;
- ret = thermal_zone_get_temp(tz, &temp);
+ ret = __thermal_zone_get_temp(tz, &temp);
if (ret) {
if (ret != -EAGAIN)
dev_warn(&tz->device,
@@ -403,10 +404,8 @@ static void update_temperature(struct thermal_zone_device *tz)
return;
}
- mutex_lock(&tz->lock);
tz->last_temperature = tz->temperature;
tz->temperature = temp;
- mutex_unlock(&tz->lock);
trace_thermal_temperature(tz);
@@ -423,6 +422,31 @@ static void thermal_zone_device_init(struct thermal_zone_device *tz)
pos->initialized = false;
}
+void __thermal_zone_device_update(struct thermal_zone_device *tz,
+ enum thermal_notify_event event)
+{
+ int count;
+
+ if (should_stop_polling(tz))
+ return;
+
+ if (atomic_read(&in_suspend))
+ return;
+
+ if (WARN_ONCE(!tz->ops->get_temp,
+ "'%s' must not be called without 'get_temp' ops set\n", __func__))
+ return;
+
+ update_temperature(tz);
+
+ thermal_zone_set_trips(tz);
+
+ tz->notify_event = event;
+
+ for (count = 0; count < tz->num_trips; count++)
+ handle_thermal_trip(tz, count);
+}
+
static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
enum thermal_device_mode mode)
{
@@ -431,10 +455,12 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
mutex_lock(&tz->lock);
/* do nothing if mode isn't changing */
- if (mode == tz->mode) {
- mutex_unlock(&tz->lock);
+ if (mode == tz->mode)
+ goto unlock;
- return ret;
+ if (!tz->ops) {
+ ret = -EINVAL;
+ goto unlock;
}
if (tz->ops->change_mode)
@@ -443,15 +469,15 @@ static int thermal_zone_device_set_mode(struct thermal_zone_device *tz,
if (!ret)
tz->mode = mode;
- mutex_unlock(&tz->lock);
-
- thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
+ __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
if (mode == THERMAL_DEVICE_ENABLED)
thermal_notify_tz_enable(tz->id);
else
thermal_notify_tz_disable(tz->id);
+unlock:
+ mutex_unlock(&tz->lock);
return ret;
}
@@ -469,40 +495,21 @@ EXPORT_SYMBOL_GPL(thermal_zone_device_disable);
int thermal_zone_device_is_enabled(struct thermal_zone_device *tz)
{
- enum thermal_device_mode mode;
+ bool enabled;
mutex_lock(&tz->lock);
-
- mode = tz->mode;
-
+ enabled = __thermal_zone_device_is_enabled(tz);
mutex_unlock(&tz->lock);
- return mode == THERMAL_DEVICE_ENABLED;
+ return enabled;
}
void thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event)
{
- int count;
-
- if (should_stop_polling(tz))
- return;
-
- if (atomic_read(&in_suspend))
- return;
-
- if (WARN_ONCE(!tz->ops->get_temp, "'%s' must not be called without "
- "'get_temp' ops set\n", __func__))
- return;
-
- update_temperature(tz);
-
- thermal_zone_set_trips(tz);
-
- tz->notify_event = event;
-
- for (count = 0; count < tz->num_trips; count++)
- handle_thermal_trip(tz, count);
+ mutex_lock(&tz->lock);
+ __thermal_zone_device_update(tz, event);
+ mutex_unlock(&tz->lock);
}
EXPORT_SYMBOL_GPL(thermal_zone_device_update);
@@ -779,6 +786,7 @@ static void thermal_release(struct device *dev)
sizeof("thermal_zone") - 1)) {
tz = to_thermal_zone(dev);
thermal_zone_destroy_device_groups(tz);
+ mutex_destroy(&tz->lock);
kfree(tz);
} else if (!strncmp(dev_name(dev), "cooling_device",
sizeof("cooling_device") - 1)) {
@@ -1397,10 +1405,19 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
thermal_remove_hwmon_sysfs(tz);
ida_free(&thermal_tz_ida, tz->id);
ida_destroy(&tz->ida);
- mutex_destroy(&tz->lock);
device_unregister(&tz->device);
thermal_notify_tz_delete(tz_id);
+
+ /*
+ * tz->ops is not reference counted, and the caller will likely
+ * release it. Make sure it is no longer used after this call returns.
+ * Note that we can not call mutex_destroy() on the mutex since the
+ * tz device may still be in use.
+ */
+ mutex_lock(&tz->lock);
+ tz->ops = NULL;
+ mutex_unlock(&tz->lock);
}
EXPORT_SYMBOL_GPL(thermal_zone_device_unregister);
@@ -109,9 +109,12 @@ int thermal_register_governor(struct thermal_governor *);
void thermal_unregister_governor(struct thermal_governor *);
int thermal_zone_device_set_policy(struct thermal_zone_device *, char *);
int thermal_build_list_of_policies(char *buf);
+void __thermal_zone_device_update(struct thermal_zone_device *tz,
+ enum thermal_notify_event event);
/* Helpers */
void thermal_zone_set_trips(struct thermal_zone_device *tz);
+int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
/* sysfs I/F */
int thermal_zone_create_device_groups(struct thermal_zone_device *, int);
@@ -65,27 +65,26 @@ get_thermal_instance(struct thermal_zone_device *tz,
EXPORT_SYMBOL(get_thermal_instance);
/**
- * thermal_zone_get_temp() - returns the temperature of a thermal zone
+ * __thermal_zone_get_temp() - returns the temperature of a thermal zone
* @tz: a valid pointer to a struct thermal_zone_device
* @temp: a valid pointer to where to store the resulting temperature.
*
* When a valid thermal zone reference is passed, it will fetch its
* temperature and fill @temp.
*
+ * Both tz and tz->ops must be valid pointers when calling this function,
+ * and tz->ops->get_temp must be set.
+ * The function must be called under tz->lock.
+ *
* Return: On success returns 0, an error code otherwise
*/
-int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
+int __thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
{
- int ret = -EINVAL;
+ int ret;
int count;
int crit_temp = INT_MAX;
enum thermal_trip_type type;
- if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
- goto exit;
-
- mutex_lock(&tz->lock);
-
ret = tz->ops->get_temp(tz, temp);
if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
@@ -107,8 +106,35 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
*temp = tz->emul_temperature;
}
+ return ret;
+}
+
+/**
+ * thermal_zone_get_temp() - returns the temperature of a thermal zone
+ * @tz: a valid pointer to a struct thermal_zone_device
+ * @temp: a valid pointer to where to store the resulting temperature.
+ *
+ * When a valid thermal zone reference is passed, it will fetch its
+ * temperature and fill @temp.
+ *
+ * Return: On success returns 0, an error code otherwise
+ */
+int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
+{
+ int ret = -EINVAL;
+
+ if (!tz || IS_ERR(tz))
+ return ret;
+
+ mutex_lock(&tz->lock);
+
+ if (!tz->ops || !tz->ops->get_temp)
+ goto unlock;
+
+ ret = __thermal_zone_get_temp(tz, temp);
+
+unlock:
mutex_unlock(&tz->lock);
-exit:
return ret;
}
EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
@@ -123,6 +149,9 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
* driver to let it set its own notification mechanism (usually an
* interrupt).
*
+ * This function must be called under tz->lock. Both tz and tz->ops
+ * must be valid pointers.
+ *
* It does not return a value
*/
void thermal_zone_set_trips(struct thermal_zone_device *tz)
@@ -132,10 +161,8 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz)
int trip_temp, hysteresis;
int i, ret;
- mutex_lock(&tz->lock);
-
if (!tz->ops->set_trips || !tz->ops->get_trip_hyst)
- goto exit;
+ return;
for (i = 0; i < tz->num_trips; i++) {
int trip_low;
@@ -154,7 +181,7 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz)
/* No need to change trip points */
if (tz->prev_low_trip == low && tz->prev_high_trip == high)
- goto exit;
+ return;
tz->prev_low_trip = low;
tz->prev_high_trip = high;
@@ -169,9 +196,6 @@ void thermal_zone_set_trips(struct thermal_zone_device *tz)
ret = tz->ops->set_trips(tz, low, high);
if (ret)
dev_err(&tz->device, "Failed to set trips: %d\n", ret);
-
-exit:
- mutex_unlock(&tz->lock);
}
static void thermal_cdev_set_cur_state(struct thermal_cooling_device *cdev,
@@ -75,13 +75,22 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
temp_crit);
struct thermal_zone_device *tz = temp->tz;
int temperature;
- int ret;
+ int ret = -EINVAL;
+
+ mutex_lock(&tz->lock);
+
+ if (!tz->ops)
+ goto unlock;
ret = tz->ops->get_crit_temp(tz, &temperature);
if (ret)
- return ret;
+ goto unlock;
- return sprintf(buf, "%d\n", temperature);
+ ret = sprintf(buf, "%d\n", temperature);
+
+unlock:
+ mutex_unlock(&tz->lock);
+ return ret;
}
@@ -469,6 +469,11 @@ static int thermal_genl_cmd_tz_get_trip(struct param *p)
mutex_lock(&tz->lock);
+ if (!tz->ops) {
+ mutex_unlock(&tz->lock);
+ return -EINVAL;
+ }
+
for (i = 0; i < tz->num_trips; i++) {
enum thermal_trip_type type;
@@ -82,28 +82,46 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
enum thermal_trip_type type;
int trip, result;
- if (!tz->ops->get_trip_type)
- return -EPERM;
-
if (sscanf(attr->attr.name, "trip_point_%d_type", &trip) != 1)
return -EINVAL;
+ mutex_lock(&tz->lock);
+
+ if (!tz->ops) {
+ result = -EINVAL;
+ goto unlock;
+ }
+
+ if (!tz->ops->get_trip_type) {
+ result = -EPERM;
+ goto unlock;
+ }
+
result = tz->ops->get_trip_type(tz, trip, &type);
if (result)
- return result;
+ goto unlock;
switch (type) {
case THERMAL_TRIP_CRITICAL:
- return sprintf(buf, "critical\n");
+ result = sprintf(buf, "critical\n");
+ break;
case THERMAL_TRIP_HOT:
- return sprintf(buf, "hot\n");
+ result = sprintf(buf, "hot\n");
+ break;
case THERMAL_TRIP_PASSIVE:
- return sprintf(buf, "passive\n");
+ result = sprintf(buf, "passive\n");
+ break;
case THERMAL_TRIP_ACTIVE:
- return sprintf(buf, "active\n");
+ result = sprintf(buf, "active\n");
+ break;
default:
- return sprintf(buf, "unknown\n");
+ result = sprintf(buf, "unknown\n");
+ break;
}
+
+unlock:
+ mutex_unlock(&tz->lock);
+ return result;
}
static ssize_t
@@ -115,34 +133,45 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
int temperature, hyst = 0;
enum thermal_trip_type type;
- if (!tz->ops->set_trip_temp)
- return -EPERM;
-
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1)
return -EINVAL;
if (kstrtoint(buf, 10, &temperature))
return -EINVAL;
+ mutex_lock(&tz->lock);
+
+ if (!tz->ops) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (!tz->ops->set_trip_temp) {
+ ret = -EPERM;
+ goto unlock;
+ }
+
ret = tz->ops->set_trip_temp(tz, trip, temperature);
if (ret)
- return ret;
+ goto unlock;
if (tz->ops->get_trip_hyst) {
ret = tz->ops->get_trip_hyst(tz, trip, &hyst);
if (ret)
- return ret;
+ goto unlock;
}
ret = tz->ops->get_trip_type(tz, trip, &type);
if (ret)
- return ret;
+ goto unlock;
thermal_notify_tz_trip_change(tz->id, trip, type, temperature, hyst);
- thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
+ __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
- return count;
+unlock:
+ mutex_unlock(&tz->lock);
+ return ret ? ret : count;
}
static ssize_t
@@ -153,18 +182,30 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
int trip, ret;
int temperature;
- if (!tz->ops->get_trip_temp)
- return -EPERM;
-
if (sscanf(attr->attr.name, "trip_point_%d_temp", &trip) != 1)
return -EINVAL;
- ret = tz->ops->get_trip_temp(tz, trip, &temperature);
+ mutex_lock(&tz->lock);
+
+ if (!tz->ops) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ if (!tz->ops->get_trip_temp) {
+ ret = -EPERM;
+ goto unlock;
+ }
+
+ ret = tz->ops->get_trip_temp(tz, trip, &temperature);
if (ret)
- return ret;
+ goto unlock;
- return sprintf(buf, "%d\n", temperature);
+ ret = sprintf(buf, "%d\n", temperature);
+
+unlock:
+ mutex_unlock(&tz->lock);
+ return ret;
}
static ssize_t
@@ -175,15 +216,24 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
int trip, ret;
int temperature;
- if (!tz->ops->set_trip_hyst)
- return -EPERM;
-
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1)
return -EINVAL;
if (kstrtoint(buf, 10, &temperature))
return -EINVAL;
+ mutex_lock(&tz->lock);
+
+ if (!tz->ops) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (!tz->ops->set_trip_hyst) {
+ ret = -EPERM;
+ goto unlock;
+ }
+
/*
* We are not doing any check on the 'temperature' value
* here. The driver implementing 'set_trip_hyst' has to
@@ -194,6 +244,8 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
if (!ret)
thermal_zone_set_trips(tz);
+unlock:
+ mutex_unlock(&tz->lock);
return ret ? ret : count;
}
@@ -205,14 +257,25 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
int trip, ret;
int temperature;
- if (!tz->ops->get_trip_hyst)
- return -EPERM;
-
if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip) != 1)
return -EINVAL;
+ mutex_lock(&tz->lock);
+
+ if (!tz->ops) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (!tz->ops->get_trip_hyst) {
+ ret = -EPERM;
+ goto unlock;
+ }
+
ret = tz->ops->get_trip_hyst(tz, trip, &temperature);
+unlock:
+ mutex_unlock(&tz->lock);
return ret ? ret : sprintf(buf, "%d\n", temperature);
}
@@ -260,17 +323,24 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
if (kstrtoint(buf, 10, &temperature))
return -EINVAL;
+ mutex_lock(&tz->lock);
+
+ if (!tz->ops) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
if (!tz->ops->set_emul_temp) {
- mutex_lock(&tz->lock);
tz->emul_temperature = temperature;
- mutex_unlock(&tz->lock);
} else {
ret = tz->ops->set_emul_temp(tz, temperature);
}
if (!ret)
- thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
+ __thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
+unlock:
+ mutex_unlock(&tz->lock);
return ret ? ret : count;
}
static DEVICE_ATTR_WO(emul_temp);
A call to thermal_zone_device_unregister() results in thermal device removal. While the thermal device itself is reference counted and protected against removal of its associated data structures, the thermal device operations are owned by the calling code and unprotected. This may result in crashes such as BUG: unable to handle page fault for address: ffffffffc04ef420 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 5d60e067 P4D 5d60e067 PUD 5d610067 PMD 110197067 PTE 0 Oops: 0000 [#1] PREEMPT SMP NOPTI CPU: 1 PID: 3209 Comm: cat Tainted: G W 5.10.136-19389-g615abc6eb807 #1 02df41ac0b12f3a64f4b34245188d8875bb3bce1 Hardware name: Google Coral/Coral, BIOS Google_Coral.10068.92.0 11/27/2018 RIP: 0010:thermal_zone_get_temp+0x26/0x73 Code: 89 c3 eb d3 0f 1f 44 00 00 55 48 89 e5 41 57 41 56 53 48 85 ff 74 50 48 89 fb 48 81 ff 00 f0 ff ff 77 44 48 8b 83 98 03 00 00 <48> 83 78 10 00 74 36 49 89 f6 4c 8d bb d8 03 00 00 4c 89 ff e8 9f RSP: 0018:ffffb3758138fd38 EFLAGS: 00010287 RAX: ffffffffc04ef410 RBX: ffff98f14d7fb000 RCX: 0000000000000000 RDX: ffff98f17cf90000 RSI: ffffb3758138fd64 RDI: ffff98f14d7fb000 RBP: ffffb3758138fd50 R08: 0000000000001000 R09: ffff98f17cf90000 R10: 0000000000000000 R11: ffffffff8dacad28 R12: 0000000000001000 R13: ffff98f1793a7d80 R14: ffff98f143231708 R15: ffff98f14d7fb018 FS: 00007ec166097800(0000) GS:ffff98f1bbd00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffffffffc04ef420 CR3: 000000010ee9a000 CR4: 00000000003506e0 Call Trace: temp_show+0x31/0x68 dev_attr_show+0x1d/0x4f sysfs_kf_seq_show+0x92/0x107 seq_read_iter+0xf5/0x3f2 vfs_read+0x205/0x379 __x64_sys_read+0x7c/0xe2 do_syscall_64+0x43/0x55 entry_SYSCALL_64_after_hwframe+0x61/0xc6 if a thermal device is removed while accesses to its device attributes are ongoing. Use the thermal device mutex to protect device operations. Clear the device operations pointer in thermal_zone_device_unregister() under protection of this mutex, and only access it while the mutex is held. Flatten and simplify device mutex operations to only acquire the mutex once and hold it instead of acquiring and releasing it several times during thermal operations. Only validate parameters once at module entry points after acquiring the mutex. Execute governor operations under mutex instead of expecting governors to acquire and release it. Signed-off-by: Guenter Roeck <linux@roeck-us.net> --- RFC/RFT: This patch ended up being substantially more complex than I initially thought it would be. It should only be applied after extensive review and testing (I don't think it is 6.1 material). I tested it as much as I could with Chromebooks using chromeos-5.10 and chromeos-5.15. Plan is to apply it to those branches as well as to older ChromeOS kernel branches, but I would like to get some level of confidence that this is the right approach before doing that. drivers/thermal/gov_bang_bang.c | 8 -- drivers/thermal/gov_fair_share.c | 3 - drivers/thermal/gov_power_allocator.c | 19 +--- drivers/thermal/gov_step_wise.c | 8 -- drivers/thermal/gov_user_space.c | 2 - drivers/thermal/thermal_core.c | 97 +++++++++++-------- drivers/thermal/thermal_core.h | 3 + drivers/thermal/thermal_helpers.c | 56 ++++++++--- drivers/thermal/thermal_hwmon.c | 15 ++- drivers/thermal/thermal_netlink.c | 5 + drivers/thermal/thermal_sysfs.c | 134 ++++++++++++++++++++------ 11 files changed, 223 insertions(+), 127 deletions(-)