From e38e726df9e1fa7601f0dd1fe67c0c4a84756ff3 Mon Sep 17 00:00:00 2001 From: Junjie Wu Date: Wed, 8 Jul 2015 10:19:34 -0700 Subject: [PATCH] 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 --- drivers/devfreq/governor_cache_hwmon.c | 23 ++++++++++++++++++----- 1 file 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");