thermal-core: Use rcu for accessing sensor list
Sensor temperature read function traverses the sensor list every time without using any lock. If the list is undergoing mutation when it is traversed, it may result in memory violation. For example, the new element could have added to the list before updating the next pointer due to out of order execution. This could result in either NULL pointer dereference or unassigned memory access violation. Using a lock every time when temperature is read may mutually exclude it from list mutation, but it impacts the performance. Sensor addition is an infrequent operation when compared to sensor temperature read. So adding a lock to protect against this infrequent operation will add overhead and deter performance. Use of list_for_each_entry_safe() function for traversing, won't help. Because this function can help only if the traversal loop removes the current element, that is pointed by this iteration. Use RCU when traversing and updating the list to avoid locks. RCU ensures list traversal safe addition or deletion happens and doesn't block the frequent read operation. Replace the list_for_each_entry_safe() function with rcu safe list traversal function list_for_each_entry_rcu(), wherever it is applicable. Change-Id: I4c05fefa6906ecad408dfd4407b60bc051366f8c Signed-off-by: Ram Chandrasekar <rkumbako@codeaurora.org>
This commit is contained in:
parent
b979f780bf
commit
c975089fd5
1 changed files with 28 additions and 19 deletions
|
@ -212,29 +212,38 @@ static DEFINE_MUTEX(sensor_list_lock);
|
|||
|
||||
static struct sensor_info *get_sensor(uint32_t sensor_id)
|
||||
{
|
||||
struct sensor_info *pos, *var;
|
||||
struct sensor_info *pos = NULL, *matching_sensor = NULL;
|
||||
|
||||
list_for_each_entry_safe(pos, var, &sensor_info_list, sensor_list) {
|
||||
if (pos->sensor_id == sensor_id)
|
||||
return pos;
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(pos, &sensor_info_list, sensor_list) {
|
||||
if (pos->sensor_id == sensor_id) {
|
||||
matching_sensor = pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
return matching_sensor;
|
||||
}
|
||||
|
||||
int sensor_get_id(char *name)
|
||||
{
|
||||
struct sensor_info *pos, *var;
|
||||
struct sensor_info *pos = NULL;
|
||||
int matching_id = -ENODEV;
|
||||
|
||||
if (!name)
|
||||
return -ENODEV;
|
||||
return matching_id;
|
||||
|
||||
list_for_each_entry_safe(pos, var, &sensor_info_list, sensor_list) {
|
||||
if (!strcmp(pos->tz->type, name))
|
||||
return pos->sensor_id;
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(pos, &sensor_info_list, sensor_list) {
|
||||
if (!strcmp(pos->tz->type, name)) {
|
||||
matching_id = pos->sensor_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return -ENODEV;
|
||||
return matching_id;
|
||||
}
|
||||
EXPORT_SYMBOL(sensor_get_id);
|
||||
|
||||
|
@ -572,7 +581,7 @@ int sensor_init(struct thermal_zone_device *tz)
|
|||
sensor->max_idx = -1;
|
||||
sensor->min_idx = -1;
|
||||
mutex_init(&sensor->lock);
|
||||
INIT_LIST_HEAD(&sensor->sensor_list);
|
||||
INIT_LIST_HEAD_RCU(&sensor->sensor_list);
|
||||
INIT_LIST_HEAD_RCU(&sensor->threshold_list);
|
||||
INIT_LIST_HEAD(&tz->tz_threshold[0].list);
|
||||
INIT_LIST_HEAD(&tz->tz_threshold[1].list);
|
||||
|
@ -582,7 +591,7 @@ int sensor_init(struct thermal_zone_device *tz)
|
|||
tz->tz_threshold[1].notify = tz_notify_trip;
|
||||
tz->tz_threshold[1].data = tz;
|
||||
tz->tz_threshold[1].trip = THERMAL_TRIP_CONFIGURABLE_LOW;
|
||||
list_add(&sensor->sensor_list, &sensor_info_list);
|
||||
list_add_rcu(&sensor->sensor_list, &sensor_info_list);
|
||||
INIT_WORK(&sensor->work, sensor_update_work);
|
||||
init_completion(&sensor->sysfs_notify_complete);
|
||||
sensor->sysfs_notify_thread = kthread_run(sensor_sysfs_notify,
|
||||
|
@ -2378,7 +2387,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
|
|||
}
|
||||
|
||||
mutex_lock(&thermal_list_lock);
|
||||
list_add_tail(&tz->node, &thermal_tz_list);
|
||||
list_add_tail_rcu(&tz->node, &thermal_tz_list);
|
||||
sensor_init(tz);
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
|
||||
|
@ -2426,7 +2435,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
|
|||
mutex_unlock(&thermal_list_lock);
|
||||
return;
|
||||
}
|
||||
list_del(&tz->node);
|
||||
list_del_rcu(&tz->node);
|
||||
|
||||
/* Unbind all cdevs associated with 'this' thermal zone */
|
||||
list_for_each_entry(cdev, &thermal_cdev_list, node) {
|
||||
|
@ -2464,7 +2473,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
|
|||
flush_work(&tz->sensor.work);
|
||||
kthread_stop(tz->sensor.sysfs_notify_thread);
|
||||
mutex_lock(&thermal_list_lock);
|
||||
list_del(&tz->sensor.sensor_list);
|
||||
list_del_rcu(&tz->sensor.sensor_list);
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
|
||||
idr_destroy(&tz->idr);
|
||||
|
@ -2492,13 +2501,13 @@ struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name)
|
|||
if (!name)
|
||||
goto exit;
|
||||
|
||||
mutex_lock(&thermal_list_lock);
|
||||
list_for_each_entry(pos, &thermal_tz_list, node)
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(pos, &thermal_tz_list, node)
|
||||
if (!strncasecmp(name, pos->type, THERMAL_NAME_LENGTH)) {
|
||||
found++;
|
||||
ref = pos;
|
||||
}
|
||||
mutex_unlock(&thermal_list_lock);
|
||||
rcu_read_unlock();
|
||||
|
||||
/* nothing has been found, thus an error code for it */
|
||||
if (found == 0)
|
||||
|
|
Loading…
Add table
Reference in a new issue