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: Mike Tipton <mdtipton@codeaurora.org>
This commit is contained in:
Mike Tipton 2020-10-06 17:54:09 -07:00 committed by Mike Tipton
parent 2ffa8360f4
commit 9bb68c3604

View File

@ -8,6 +8,8 @@
#include "vdd-class.h"
static DEFINE_MUTEX(vdd_lock);
/*
* Aggregate the vdd_class level votes and call regulator framework functions
* to enforce the highest vote.
@ -86,12 +88,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);
@ -121,12 +126,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);