diff options
| author | Junjie Wu <junjiew@codeaurora.org> | 2015-07-08 10:19:34 -0700 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:04:21 -0700 |
| commit | e38e726df9e1fa7601f0dd1fe67c0c4a84756ff3 (patch) | |
| tree | d8dbd0495b0c4558fd2e1fc4f453d22a88713f20 /drivers/devfreq | |
| parent | a61d19ec530236868ffab97103d0a8df5949e7b4 (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.c | 23 |
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(®ister_lock); if (!use_cnt) { ret = devfreq_add_governor(&devfreq_cache_hwmon); if (!ret) use_cnt++; } - mutex_unlock(&state_lock); + mutex_unlock(®ister_lock); if (!ret) { dev_info(dev, "Cache HWmon governor registered.\n"); |
