aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichele Di Giorgio <michele.digiorgio@arm.com>2016-06-02 10:25:31 -0400
committerZhang Rui <rui.zhang@intel.com>2016-08-07 22:57:39 -0400
commitd0b7306d203c82e7c04d6eb066ca4898f016ebdd (patch)
treedc30ec87806e7e016e78ca4739791526349f5d13
parent29b4817d4018df78086157ea3a55c1d9424a7cfc (diff)
thermal: fix race condition when updating cooling device
When multiple thermal zones are bound to the same cooling device, multiple kernel threads may want to update the cooling device state by calling thermal_cdev_update(). Having cdev not protected by a mutex can lead to a race condition. Consider the following situation with two kernel threads k1 and k2: Thread k1 Thread k2 || || call thermal_cdev_update() || ... || set_cur_state(cdev, target); call power_actor_set_power() || ... || instance->target = state; || cdev->updated = false; || || cdev->updated = true; || // completes execution call thermal_cdev_update() || // cdev->updated == true || return; || \/ time k2 has already looped through the thermal instances looking for the deepest cooling device state and is preempted right before setting cdev->updated to true. Now, k1 runs, modifies the thermal instance state and sets cdev->updated to false. Then, k1 is preempted and k2 continues the execution by setting cdev->updated to true, therefore preventing k1 from performing the update. Notice that this is not an issue if k2 looks at the instance->target modified by k1 "after" it is assigned by k1. In fact, in this case the update will happen anyway and k1 can safely return immediately from thermal_cdev_update(). This may lead to a situation where a thermal governor never updates the cooling device. For example, this is the case for the step_wise governor: when calling the function thermal_zone_trip_update(), the governor may always get a new state equal to the old one (which, however, wasn't notified to the cooling device) and will therefore skip the update. CC: Zhang Rui <rui.zhang@intel.com> CC: Eduardo Valentin <edubezval@gmail.com> CC: Peter Feuerer <peter@piie.net> Reported-by: Toby Huang <toby.huang@arm.com> Signed-off-by: Michele Di Giorgio <michele.digiorgio@arm.com> Reviewed-by: Javi Merino <javi.merino@arm.com> Signed-off-by: Zhang Rui <rui.zhang@intel.com>
-rw-r--r--drivers/thermal/fair_share.c2
-rw-r--r--drivers/thermal/gov_bang_bang.c2
-rw-r--r--drivers/thermal/power_allocator.c2
-rw-r--r--drivers/thermal/step_wise.c2
-rw-r--r--drivers/thermal/thermal_core.c10
5 files changed, 15 insertions, 3 deletions
diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/fair_share.c
index 34fe36504a55..68bd1b569118 100644
--- a/drivers/thermal/fair_share.c
+++ b/drivers/thermal/fair_share.c
@@ -116,7 +116,9 @@ static int fair_share_throttle(struct thermal_zone_device *tz, int trip)
116 instance->target = get_target_state(tz, cdev, percentage, 116 instance->target = get_target_state(tz, cdev, percentage,
117 cur_trip_level); 117 cur_trip_level);
118 118
119 mutex_lock(&instance->cdev->lock);
119 instance->cdev->updated = false; 120 instance->cdev->updated = false;
121 mutex_unlock(&instance->cdev->lock);
120 thermal_cdev_update(cdev); 122 thermal_cdev_update(cdev);
121 } 123 }
122 return 0; 124 return 0;
diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c
index fc52016d4e85..bb118a152cbb 100644
--- a/drivers/thermal/gov_bang_bang.c
+++ b/drivers/thermal/gov_bang_bang.c
@@ -71,7 +71,9 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
71 dev_dbg(&instance->cdev->device, "target=%d\n", 71 dev_dbg(&instance->cdev->device, "target=%d\n",
72 (int)instance->target); 72 (int)instance->target);
73 73
74 mutex_lock(&instance->cdev->lock);
74 instance->cdev->updated = false; /* cdev needs update */ 75 instance->cdev->updated = false; /* cdev needs update */
76 mutex_unlock(&instance->cdev->lock);
75 } 77 }
76 78
77 mutex_unlock(&tz->lock); 79 mutex_unlock(&tz->lock);
diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c
index 2f1a863a8e15..b4d3116cfdaf 100644
--- a/drivers/thermal/power_allocator.c
+++ b/drivers/thermal/power_allocator.c
@@ -529,7 +529,9 @@ static void allow_maximum_power(struct thermal_zone_device *tz)
529 continue; 529 continue;
530 530
531 instance->target = 0; 531 instance->target = 0;
532 mutex_lock(&instance->cdev->lock);
532 instance->cdev->updated = false; 533 instance->cdev->updated = false;
534 mutex_unlock(&instance->cdev->lock);
533 thermal_cdev_update(instance->cdev); 535 thermal_cdev_update(instance->cdev);
534 } 536 }
535} 537}
diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c
index ea9366ad3e6b..bcef2e7c4ec9 100644
--- a/drivers/thermal/step_wise.c
+++ b/drivers/thermal/step_wise.c
@@ -175,7 +175,9 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
175 update_passive_instance(tz, trip_type, -1); 175 update_passive_instance(tz, trip_type, -1);
176 176
177 instance->initialized = true; 177 instance->initialized = true;
178 mutex_lock(&instance->cdev->lock);
178 instance->cdev->updated = false; /* cdev needs update */ 179 instance->cdev->updated = false; /* cdev needs update */
180 mutex_unlock(&instance->cdev->lock);
179 } 181 }
180 182
181 mutex_unlock(&tz->lock); 183 mutex_unlock(&tz->lock);
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 5133cd1e10b7..e2fc6161dded 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -1093,7 +1093,9 @@ int power_actor_set_power(struct thermal_cooling_device *cdev,
1093 return ret; 1093 return ret;
1094 1094
1095 instance->target = state; 1095 instance->target = state;
1096 mutex_lock(&cdev->lock);
1096 cdev->updated = false; 1097 cdev->updated = false;
1098 mutex_unlock(&cdev->lock);
1097 thermal_cdev_update(cdev); 1099 thermal_cdev_update(cdev);
1098 1100
1099 return 0; 1101 return 0;
@@ -1623,11 +1625,13 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev)
1623 struct thermal_instance *instance; 1625 struct thermal_instance *instance;
1624 unsigned long target = 0; 1626 unsigned long target = 0;
1625 1627
1628 mutex_lock(&cdev->lock);
1626 /* cooling device is updated*/ 1629 /* cooling device is updated*/
1627 if (cdev->updated) 1630 if (cdev->updated) {
1631 mutex_unlock(&cdev->lock);
1628 return; 1632 return;
1633 }
1629 1634
1630 mutex_lock(&cdev->lock);
1631 /* Make sure cdev enters the deepest cooling state */ 1635 /* Make sure cdev enters the deepest cooling state */
1632 list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { 1636 list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) {
1633 dev_dbg(&cdev->device, "zone%d->target=%lu\n", 1637 dev_dbg(&cdev->device, "zone%d->target=%lu\n",
@@ -1637,9 +1641,9 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev)
1637 if (instance->target > target) 1641 if (instance->target > target)
1638 target = instance->target; 1642 target = instance->target;
1639 } 1643 }
1640 mutex_unlock(&cdev->lock);
1641 cdev->ops->set_cur_state(cdev, target); 1644 cdev->ops->set_cur_state(cdev, target);
1642 cdev->updated = true; 1645 cdev->updated = true;
1646 mutex_unlock(&cdev->lock);
1643 trace_cdev_update(cdev, target); 1647 trace_cdev_update(cdev, target);
1644 dev_dbg(&cdev->device, "set to state %lu\n", target); 1648 dev_dbg(&cdev->device, "set to state %lu\n", target);
1645} 1649}