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(&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");