summaryrefslogtreecommitdiff
path: root/drivers/devfreq
diff options
context:
space:
mode:
authorJunjie Wu <junjiew@codeaurora.org>2015-07-08 10:19:34 -0700
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 20:04:21 -0700
commite38e726df9e1fa7601f0dd1fe67c0c4a84756ff3 (patch)
treed8dbd0495b0c4558fd2e1fc4f453d22a88713f20 /drivers/devfreq
parenta61d19ec530236868ffab97103d0a8df5949e7b4 (diff)
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>
Diffstat (limited to 'drivers/devfreq')
-rw-r--r--drivers/devfreq/governor_cache_hwmon.c23
1 files changed, 18 insertions, 5 deletions
diff --git a/drivers/devfreq/governor_cache_hwmon.c b/drivers/devfreq/governor_cache_hwmon.c
index 7aa58c29a5ed..9973aeac4679 100644
--- a/drivers/devfreq/governor_cache_hwmon.c
+++ b/drivers/devfreq/governor_cache_hwmon.c
@@ -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
* 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 int use_cnt;
-static DEFINE_MUTEX(state_lock);
+static DEFINE_MUTEX(register_lock);
+
+static DEFINE_MUTEX(monitor_lock);
#define show_attr(name) \
static ssize_t show_##name(struct device *dev, \
@@ -185,8 +187,12 @@ int update_cache_hwmon(struct cache_hwmon *hwmon)
node = df->data;
if (!node)
return -ENODEV;
- if (!node->mon_started)
+
+ mutex_lock(&monitor_lock);
+ if (!node->mon_started) {
+ mutex_unlock(&monitor_lock);
return -EBUSY;
+ }
dev_dbg(df->dev.parent, "Got update request\n");
devfreq_monitor_stop(df);
@@ -216,6 +222,7 @@ int update_cache_hwmon(struct cache_hwmon *hwmon)
devfreq_monitor_start(df);
+ mutex_unlock(&monitor_lock);
return 0;
}
@@ -289,8 +296,10 @@ static int start_monitoring(struct devfreq *df)
goto err_start;
}
+ mutex_lock(&monitor_lock);
devfreq_monitor_start(df);
node->mon_started = true;
+ mutex_unlock(&monitor_lock);
ret = sysfs_create_group(&df->dev.kobj, &dev_attr_group);
if (ret) {
@@ -301,8 +310,10 @@ static int start_monitoring(struct devfreq *df)
return 0;
sysfs_fail:
+ mutex_lock(&monitor_lock);
node->mon_started = false;
devfreq_monitor_stop(df);
+ mutex_unlock(&monitor_lock);
hw->stop_hwmon(hw);
err_start:
df->data = node->orig_data;
@@ -317,8 +328,10 @@ static void stop_monitoring(struct devfreq *df)
struct cache_hwmon *hw = node->hw;
sysfs_remove_group(&df->dev.kobj, &dev_attr_group);
+ mutex_lock(&monitor_lock);
node->mon_started = false;
devfreq_monitor_stop(df);
+ mutex_unlock(&monitor_lock);
hw->stop_hwmon(hw);
df->data = node->orig_data;
node->orig_data = NULL;
@@ -391,13 +404,13 @@ int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon)
node->hw = hwmon;
node->attr_grp = &dev_attr_group;
- mutex_lock(&state_lock);
+ mutex_lock(&register_lock);
if (!use_cnt) {
ret = devfreq_add_governor(&devfreq_cache_hwmon);
if (!ret)
use_cnt++;
}
- mutex_unlock(&state_lock);
+ mutex_unlock(&register_lock);
if (!ret) {
dev_info(dev, "Cache HWmon governor registered.\n");