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

Commit 70b45e9f authored by Junjie Wu's avatar Junjie Wu
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 cf1a92bc
Loading
Loading
Loading
Loading
+18 −5
Original line number Diff line number Diff line
/*
 * 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(&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");