Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a3b03ca5 authored by Junjie Wu's avatar Junjie Wu Committed by Amir Vajid
Browse files

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: default avatarJunjie Wu <junjiew@codeaurora.org>
parent b0dfbcd1
Loading
Loading
Loading
Loading
+18 −5
Original line number Diff line number Diff line
// 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 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 name##_show(struct device *dev,				\
@@ -171,8 +173,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);
@@ -202,6 +208,7 @@ int update_cache_hwmon(struct cache_hwmon *hwmon)

	devfreq_monitor_start(df);

	mutex_unlock(&monitor_lock);
	return 0;
}

@@ -290,8 +297,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 < 0) {
@@ -302,8 +311,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;
@@ -318,8 +329,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;
@@ -388,13 +401,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");