PM / devfreq: governor_cache_hwmon: Fix race in monitor start/stop
Some cache_hwmon devices can have interrupts firing at any time. The interrupt handler would stop devfreq monitor, update its vote and restart the monitor again. This introduces a race if devfreq_supend/resume() or devfreq_interval_update() is called at the same time. Since devfreq_monitor_start() re-initializes the work, it could cause corruption while the work is being used elsewhere. Protect governor monitor start/stops with a new lock. Change-Id: I143aaaea86494b4c617df46e2c521a19b43861d5 Signed-off-by: Junjie Wu <junjiew@codeaurora.org>
This commit is contained in:
parent
1af3fa893f
commit
d2dd9e751b
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 and
|
* it under the terms of the GNU General Public License version 2 and
|
||||||
|
@ -53,7 +53,9 @@ static LIST_HEAD(cache_hwmon_list);
|
||||||
static DEFINE_MUTEX(list_lock);
|
static DEFINE_MUTEX(list_lock);
|
||||||
|
|
||||||
static int use_cnt;
|
static int use_cnt;
|
||||||
static DEFINE_MUTEX(state_lock);
|
static DEFINE_MUTEX(register_lock);
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(monitor_lock);
|
||||||
|
|
||||||
#define show_attr(name) \
|
#define show_attr(name) \
|
||||||
static ssize_t show_##name(struct device *dev, \
|
static ssize_t show_##name(struct device *dev, \
|
||||||
|
@ -185,8 +187,12 @@ int update_cache_hwmon(struct cache_hwmon *hwmon)
|
||||||
node = df->data;
|
node = df->data;
|
||||||
if (!node)
|
if (!node)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
if (!node->mon_started)
|
|
||||||
|
mutex_lock(&monitor_lock);
|
||||||
|
if (!node->mon_started) {
|
||||||
|
mutex_unlock(&monitor_lock);
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
dev_dbg(df->dev.parent, "Got update request\n");
|
dev_dbg(df->dev.parent, "Got update request\n");
|
||||||
devfreq_monitor_stop(df);
|
devfreq_monitor_stop(df);
|
||||||
|
@ -216,6 +222,7 @@ int update_cache_hwmon(struct cache_hwmon *hwmon)
|
||||||
|
|
||||||
devfreq_monitor_start(df);
|
devfreq_monitor_start(df);
|
||||||
|
|
||||||
|
mutex_unlock(&monitor_lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,8 +296,10 @@ static int start_monitoring(struct devfreq *df)
|
||||||
goto err_start;
|
goto err_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_lock(&monitor_lock);
|
||||||
devfreq_monitor_start(df);
|
devfreq_monitor_start(df);
|
||||||
node->mon_started = true;
|
node->mon_started = true;
|
||||||
|
mutex_unlock(&monitor_lock);
|
||||||
|
|
||||||
ret = sysfs_create_group(&df->dev.kobj, &dev_attr_group);
|
ret = sysfs_create_group(&df->dev.kobj, &dev_attr_group);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -301,8 +310,10 @@ static int start_monitoring(struct devfreq *df)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
sysfs_fail:
|
sysfs_fail:
|
||||||
|
mutex_lock(&monitor_lock);
|
||||||
node->mon_started = false;
|
node->mon_started = false;
|
||||||
devfreq_monitor_stop(df);
|
devfreq_monitor_stop(df);
|
||||||
|
mutex_unlock(&monitor_lock);
|
||||||
hw->stop_hwmon(hw);
|
hw->stop_hwmon(hw);
|
||||||
err_start:
|
err_start:
|
||||||
df->data = node->orig_data;
|
df->data = node->orig_data;
|
||||||
|
@ -317,8 +328,10 @@ static void stop_monitoring(struct devfreq *df)
|
||||||
struct cache_hwmon *hw = node->hw;
|
struct cache_hwmon *hw = node->hw;
|
||||||
|
|
||||||
sysfs_remove_group(&df->dev.kobj, &dev_attr_group);
|
sysfs_remove_group(&df->dev.kobj, &dev_attr_group);
|
||||||
|
mutex_lock(&monitor_lock);
|
||||||
node->mon_started = false;
|
node->mon_started = false;
|
||||||
devfreq_monitor_stop(df);
|
devfreq_monitor_stop(df);
|
||||||
|
mutex_unlock(&monitor_lock);
|
||||||
hw->stop_hwmon(hw);
|
hw->stop_hwmon(hw);
|
||||||
df->data = node->orig_data;
|
df->data = node->orig_data;
|
||||||
node->orig_data = NULL;
|
node->orig_data = NULL;
|
||||||
|
@ -391,13 +404,13 @@ int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon)
|
||||||
node->hw = hwmon;
|
node->hw = hwmon;
|
||||||
node->attr_grp = &dev_attr_group;
|
node->attr_grp = &dev_attr_group;
|
||||||
|
|
||||||
mutex_lock(&state_lock);
|
mutex_lock(®ister_lock);
|
||||||
if (!use_cnt) {
|
if (!use_cnt) {
|
||||||
ret = devfreq_add_governor(&devfreq_cache_hwmon);
|
ret = devfreq_add_governor(&devfreq_cache_hwmon);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
use_cnt++;
|
use_cnt++;
|
||||||
}
|
}
|
||||||
mutex_unlock(&state_lock);
|
mutex_unlock(®ister_lock);
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
dev_info(dev, "Cache HWmon governor registered.\n");
|
dev_info(dev, "Cache HWmon governor registered.\n");
|
||||||
|
|
Loading…
Reference in New Issue