Skip to content

Commit

Permalink
PM / OPP: Update OPP users to put reference
Browse files Browse the repository at this point in the history
This patch updates dev_pm_opp_find_freq_*() routines to get a reference
to the OPPs returned by them.

Also updates the users of dev_pm_opp_find_freq_*() routines to call
dev_pm_opp_put() after they are done using the OPPs.

As it is guaranteed the that OPPs wouldn't get freed while being used,
the RCU read side locking present with the users isn't required anymore.
Drop it as well.

This patch also updates all users of devfreq_recommended_opp() which was
returning an OPP received from the OPP core.

Note that some of the OPP core routines have gained
rcu_read_{lock|unlock}() calls, as those still use RCU specific APIs
within them.

Signed-off-by: Viresh Kumar <[email protected]>
Reviewed-by: Chanwoo Choi <[email protected]> [Devfreq]
Signed-off-by: Rafael J. Wysocki <[email protected]>
  • Loading branch information
vireshk authored and rafaeljw committed Jan 30, 2017
1 parent 7034764 commit 8a31d9d
Show file tree
Hide file tree
Showing 15 changed files with 110 additions and 153 deletions.
5 changes: 2 additions & 3 deletions arch/arm/mach-omap2/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,16 @@ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name,
freq = clk_get_rate(clk);
clk_put(clk);

rcu_read_lock();
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
if (IS_ERR(opp)) {
rcu_read_unlock();
pr_err("%s: unable to find boot up OPP for vdd_%s\n",
__func__, vdd_name);
goto exit;
}

bootup_volt = dev_pm_opp_get_voltage(opp);
rcu_read_unlock();
dev_pm_opp_put(opp);

if (!bootup_volt) {
pr_err("%s: unable to find voltage corresponding to the bootup OPP for vdd_%s\n",
__func__, vdd_name);
Expand Down
114 changes: 63 additions & 51 deletions drivers/base/power/opp/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ do { \
"opp_table_lock protection"); \
} while (0)

static void dev_pm_opp_get(struct dev_pm_opp *opp);

static struct opp_device *_find_opp_dev(const struct device *dev,
struct opp_table *opp_table)
{
Expand Down Expand Up @@ -94,28 +96,21 @@ struct opp_table *_find_opp_table(struct device *dev)
* return 0
*
* This is useful only for devices with single power supply.
*
* Locking: This function must be called under rcu_read_lock(). opp is a rcu
* protected pointer. This means that opp which could have been fetched by
* opp_find_freq_{exact,ceil,floor} functions is valid as long as we are
* under RCU lock. The pointer returned by the opp_find_freq family must be
* used in the same section as the usage of this function with the pointer
* prior to unlocking with rcu_read_unlock() to maintain the integrity of the
* pointer.
*/
unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp)
{
struct dev_pm_opp *tmp_opp;
unsigned long v = 0;

opp_rcu_lockdep_assert();
rcu_read_lock();

tmp_opp = rcu_dereference(opp);
if (IS_ERR_OR_NULL(tmp_opp))
pr_err("%s: Invalid parameters\n", __func__);
else
v = tmp_opp->supplies[0].u_volt;

rcu_read_unlock();
return v;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage);
Expand All @@ -126,28 +121,21 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage);
*
* Return: frequency in hertz corresponding to the opp, else
* return 0
*
* Locking: This function must be called under rcu_read_lock(). opp is a rcu
* protected pointer. This means that opp which could have been fetched by
* opp_find_freq_{exact,ceil,floor} functions is valid as long as we are
* under RCU lock. The pointer returned by the opp_find_freq family must be
* used in the same section as the usage of this function with the pointer
* prior to unlocking with rcu_read_unlock() to maintain the integrity of the
* pointer.
*/
unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp)
{
struct dev_pm_opp *tmp_opp;
unsigned long f = 0;

opp_rcu_lockdep_assert();
rcu_read_lock();

tmp_opp = rcu_dereference(opp);
if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available)
pr_err("%s: Invalid parameters\n", __func__);
else
f = tmp_opp->rate;

rcu_read_unlock();
return f;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq);
Expand All @@ -161,28 +149,24 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq);
* quickly. Running on them for longer times may overheat the chip.
*
* Return: true if opp is turbo opp, else false.
*
* Locking: This function must be called under rcu_read_lock(). opp is a rcu
* protected pointer. This means that opp which could have been fetched by
* opp_find_freq_{exact,ceil,floor} functions is valid as long as we are
* under RCU lock. The pointer returned by the opp_find_freq family must be
* used in the same section as the usage of this function with the pointer
* prior to unlocking with rcu_read_unlock() to maintain the integrity of the
* pointer.
*/
bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp)
{
struct dev_pm_opp *tmp_opp;
bool turbo;

opp_rcu_lockdep_assert();
rcu_read_lock();

tmp_opp = rcu_dereference(opp);
if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) {
pr_err("%s: Invalid parameters\n", __func__);
return false;
}

return tmp_opp->turbo;
turbo = tmp_opp->turbo;

rcu_read_unlock();
return turbo;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo);

Expand Down Expand Up @@ -410,11 +394,8 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count);
* This provides a mechanism to enable an opp which is not available currently
* or the opposite as well.
*
* Locking: This function must be called under rcu_read_lock(). opp is a rcu
* protected pointer. The reason for the same is that the opp pointer which is
* returned will remain valid for use with opp_get_{voltage, freq} only while
* under the locked area. The pointer returned must be used prior to unlocking
* with rcu_read_unlock() to maintain the integrity of the pointer.
* The callers are required to call dev_pm_opp_put() for the returned OPP after
* use.
*/
struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
unsigned long freq,
Expand All @@ -423,24 +404,30 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
struct opp_table *opp_table;
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);

opp_rcu_lockdep_assert();
rcu_read_lock();

opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
int r = PTR_ERR(opp_table);

dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r);
rcu_read_unlock();
return ERR_PTR(r);
}

list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available == available &&
temp_opp->rate == freq) {
opp = temp_opp;

/* Increment the reference count of OPP */
dev_pm_opp_get(opp);
break;
}
}

rcu_read_unlock();

return opp;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact);
Expand All @@ -454,6 +441,9 @@ static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table,
if (temp_opp->available && temp_opp->rate >= *freq) {
opp = temp_opp;
*freq = opp->rate;

/* Increment the reference count of OPP */
dev_pm_opp_get(opp);
break;
}
}
Expand All @@ -476,29 +466,33 @@ static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table,
* ERANGE: no match found for search
* ENODEV: if device not found in list of registered devices
*
* Locking: This function must be called under rcu_read_lock(). opp is a rcu
* protected pointer. The reason for the same is that the opp pointer which is
* returned will remain valid for use with opp_get_{voltage, freq} only while
* under the locked area. The pointer returned must be used prior to unlocking
* with rcu_read_unlock() to maintain the integrity of the pointer.
* The callers are required to call dev_pm_opp_put() for the returned OPP after
* use.
*/
struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
unsigned long *freq)
{
struct opp_table *opp_table;

opp_rcu_lockdep_assert();
struct dev_pm_opp *opp;

if (!dev || !freq) {
dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
return ERR_PTR(-EINVAL);
}

rcu_read_lock();

opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table))
if (IS_ERR(opp_table)) {
rcu_read_unlock();
return ERR_CAST(opp_table);
}

opp = _find_freq_ceil(opp_table, freq);

return _find_freq_ceil(opp_table, freq);
rcu_read_unlock();

return opp;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil);

Expand All @@ -517,28 +511,27 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil);
* ERANGE: no match found for search
* ENODEV: if device not found in list of registered devices
*
* Locking: This function must be called under rcu_read_lock(). opp is a rcu
* protected pointer. The reason for the same is that the opp pointer which is
* returned will remain valid for use with opp_get_{voltage, freq} only while
* under the locked area. The pointer returned must be used prior to unlocking
* with rcu_read_unlock() to maintain the integrity of the pointer.
* The callers are required to call dev_pm_opp_put() for the returned OPP after
* use.
*/
struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
unsigned long *freq)
{
struct opp_table *opp_table;
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);

opp_rcu_lockdep_assert();

if (!dev || !freq) {
dev_err(dev, "%s: Invalid argument freq=%p\n", __func__, freq);
return ERR_PTR(-EINVAL);
}

rcu_read_lock();

opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table))
if (IS_ERR(opp_table)) {
rcu_read_unlock();
return ERR_CAST(opp_table);
}

list_for_each_entry_rcu(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available) {
Expand All @@ -549,6 +542,12 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
opp = temp_opp;
}
}

/* Increment the reference count of OPP */
if (!IS_ERR(opp))
dev_pm_opp_get(opp);
rcu_read_unlock();

if (!IS_ERR(opp))
*freq = opp->rate;

Expand Down Expand Up @@ -736,6 +735,8 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
ret = PTR_ERR(opp);
dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n",
__func__, freq, ret);
if (!IS_ERR(old_opp))
dev_pm_opp_put(old_opp);
rcu_read_unlock();
return ret;
}
Expand All @@ -747,6 +748,9 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)

/* Only frequency scaling */
if (!regulators) {
dev_pm_opp_put(opp);
if (!IS_ERR(old_opp))
dev_pm_opp_put(old_opp);
rcu_read_unlock();
return _generic_set_opp_clk_only(dev, clk, old_freq, freq);
}
Expand All @@ -772,6 +776,9 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
data->new_opp.rate = freq;
memcpy(data->new_opp.supplies, opp->supplies, size);

dev_pm_opp_put(opp);
if (!IS_ERR(old_opp))
dev_pm_opp_put(old_opp);
rcu_read_unlock();

return set_opp(data);
Expand Down Expand Up @@ -967,6 +974,11 @@ static void _opp_kref_release(struct kref *kref)
dev_pm_opp_put_opp_table(opp_table);
}

static void dev_pm_opp_get(struct dev_pm_opp *opp)
{
kref_get(&opp->kref);
}

void dev_pm_opp_put(struct dev_pm_opp *opp)
{
kref_put_mutex(&opp->kref, _opp_kref_release, &opp->opp_table->lock);
Expand Down
22 changes: 6 additions & 16 deletions drivers/base/power/opp/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,6 @@
*
* WARNING: It is important for the callers to ensure refreshing their copy of
* the table if any of the mentioned functions have been invoked in the interim.
*
* Locking: The internal opp_table and opp structures are RCU protected.
* Since we just use the regular accessor functions to access the internal data
* structures, we use RCU read lock inside this function. As a result, users of
* this function DONOT need to use explicit locks for invoking.
*/
int dev_pm_opp_init_cpufreq_table(struct device *dev,
struct cpufreq_frequency_table **table)
Expand All @@ -56,19 +51,13 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
int i, max_opps, ret = 0;
unsigned long rate;

rcu_read_lock();

max_opps = dev_pm_opp_get_opp_count(dev);
if (max_opps <= 0) {
ret = max_opps ? max_opps : -ENODATA;
goto out;
}
if (max_opps <= 0)
return max_opps ? max_opps : -ENODATA;

freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_ATOMIC);
if (!freq_table) {
ret = -ENOMEM;
goto out;
}
if (!freq_table)
return -ENOMEM;

for (i = 0, rate = 0; i < max_opps; i++, rate++) {
/* find next rate */
Expand All @@ -83,6 +72,8 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
/* Is Boost/turbo opp ? */
if (dev_pm_opp_is_turbo(opp))
freq_table[i].flags = CPUFREQ_BOOST_FREQ;

dev_pm_opp_put(opp);
}

freq_table[i].driver_data = i;
Expand All @@ -91,7 +82,6 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
*table = &freq_table[0];

out:
rcu_read_unlock();
if (ret)
kfree(freq_table);

Expand Down
Loading

0 comments on commit 8a31d9d

Please sign in to comment.