PM / devfreq: bw_hwmon: Fix race condition in polling interval update

When the polling interval is updated, the delayed workqueue is cancelled
and requeued with the new polling interval. However, the bw_hwmon IRQ can
come at the same time and try to stop and restart the delayed work (in the
IRQ thread). This can cause a race where the work might be queued twice or
canceled twice and cause a crash.

Fix this race condition by suspending and resuming the HW monitor when we
are updating the polling interval. This entirely avoids the race because
suspending the HW monitor also avoid the possibility of the IRQ coming
during the polling interval update.

CRs-Fixed: 954082
Change-Id: Ic7baf2a3da4ed8f8a9023617059e22fd81c3ba45
Signed-off-by: Saravana Kannan <skannan@codeaurora.org>
This commit is contained in:
Saravana Kannan 2016-02-18 18:28:29 -08:00 committed by Jeevan Shriram
parent e9b36267d6
commit d274d4f317

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
* Copyright (c) 2013-2016, 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
@ -764,6 +764,8 @@ static int devfreq_bw_hwmon_ev_handler(struct devfreq *df,
{
int ret;
unsigned int sample_ms;
struct hwmon_node *node;
struct bw_hwmon *hw;
switch (event) {
case DEVFREQ_GOV_START:
@ -790,7 +792,22 @@ static int devfreq_bw_hwmon_ev_handler(struct devfreq *df,
sample_ms = *(unsigned int *)data;
sample_ms = max(MIN_MS, sample_ms);
sample_ms = min(MAX_MS, sample_ms);
/*
* Suspend/resume the HW monitor around the interval update
* to prevent the HW monitor IRQ from trying to change
* stop/start the delayed workqueue while the interval update
* is happening.
*/
node = df->data;
hw = node->hw;
hw->suspend_hwmon(hw);
devfreq_interval_update(df, &sample_ms);
ret = hw->resume_hwmon(hw);
if (ret) {
dev_err(df->dev.parent,
"Unable to resume HW monitor (%d)\n", ret);
return ret;
}
break;
case DEVFREQ_GOV_SUSPEND: