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

Commit 0cf6edfa authored by Mike Tipton's avatar Mike Tipton
Browse files

clk: qcom: Fix race condition when unvoting regulators in sync_state



Normally clk_vote_vdd_level() and clk_unvote_vdd_level() are called
while holding the clock framework's prepare_lock since they are called
in the prepare paths. However, we're also calling clk_unvote_vdd_level()
from qcom_cc_sync_state() when removing proxy votes. This happens
outside of the clock framework, so the prepare_lock isn't protecting
this case. If the sync_state callback fires while another thread is
voting/unvoting voltage from a normal clock call, then there's a race
condition that can result in regulators being set to the incorrect
voltage or being pre-maturely disabled.

Add a mutex to protect this case.

Change-Id: Ib979e2edaaa5824dc80f7d4f065d72ca0b1a4444
Signed-off-by: default avatarMike Tipton <mdtipton@codeaurora.org>
parent 5168327a
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -9,6 +9,8 @@
#include "vdd-class.h"
#include "clk-regmap.h"

static DEFINE_MUTEX(vdd_lock);

/*
 * Aggregate the vdd_class level votes and call regulator framework functions
 * to enforce the highest vote.
@@ -87,12 +89,15 @@ int clk_vote_vdd_level(struct clk_vdd_class_data *vdd_data, int level)
	if (level >= vdd_class->num_levels)
		return -EINVAL;

	mutex_lock(&vdd_lock);
	vdd_class->level_votes[level]++;

	ret = clk_aggregate_vdd(vdd_class);
	if (ret)
		vdd_class->level_votes[level]--;

	mutex_unlock(&vdd_lock);

	return ret;
}
EXPORT_SYMBOL(clk_vote_vdd_level);
@@ -122,12 +127,15 @@ int clk_unvote_vdd_level(struct clk_vdd_class_data *vdd_data, int level)
		return -EINVAL;
	}

	mutex_lock(&vdd_lock);
	vdd_class->level_votes[level]--;

	ret = clk_aggregate_vdd(vdd_class);
	if (ret)
		vdd_class->level_votes[level]++;

	mutex_unlock(&vdd_lock);

	return ret;
}
EXPORT_SYMBOL(clk_unvote_vdd_level);