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>
Signed-off-by: Rama Aparna Mallavarapu <aparnam@codeaurora.org>
diff --git a/drivers/devfreq/governor_cache_hwmon.c b/drivers/devfreq/governor_cache_hwmon.c
index b3200d8..7097495 100644
--- a/drivers/devfreq/governor_cache_hwmon.c
+++ b/drivers/devfreq/governor_cache_hwmon.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2014, 2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2015, 2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "cache-hwmon: " fmt
@@ -46,7 +46,9 @@
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, \
@@ -176,8 +178,12 @@
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);
@@ -207,6 +213,7 @@
devfreq_monitor_start(df);
+ mutex_unlock(&monitor_lock);
return 0;
}
@@ -279,8 +286,10 @@
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) {
@@ -291,8 +300,10 @@
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;
@@ -307,8 +318,10 @@
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;
@@ -379,13 +392,13 @@
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");