opp: Don't drop reference for an OPP table that was never parsed

dev_pm_opp_remove_table() should drop a reference to the OPP table only
if the DT OPP table was parsed earlier with a call to
dev_pm_opp_of_add_table() earlier. Else it may end up dropping the
reference to the OPP table, which was added as a result of other calls
like dev_pm_opp_set_clkname(). And would hence result in undesirable
behavior later on when caller would try to free the resource again.

Fixes: 03758d6026 ("opp: Replace list_kref with a local counter")
Reported-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Reported-by: Anders Roxell <anders.roxell@linaro.org>
Tested-by: Naresh Kamboju <naresh.kamboju@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
This commit is contained in:
Viresh Kumar 2020-08-31 13:03:06 +05:30
parent 9123e3a74e
commit 922ff0759a
2 changed files with 17 additions and 7 deletions

View File

@ -1291,13 +1291,19 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq)
}
EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
void _opp_remove_all_static(struct opp_table *opp_table)
bool _opp_remove_all_static(struct opp_table *opp_table)
{
struct dev_pm_opp *opp, *tmp;
bool ret = true;
mutex_lock(&opp_table->lock);
if (!opp_table->parsed_static_opps || --opp_table->parsed_static_opps)
if (!opp_table->parsed_static_opps) {
ret = false;
goto unlock;
}
if (--opp_table->parsed_static_opps)
goto unlock;
list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) {
@ -1307,6 +1313,8 @@ void _opp_remove_all_static(struct opp_table *opp_table)
unlock:
mutex_unlock(&opp_table->lock);
return ret;
}
/**
@ -2409,13 +2417,15 @@ void _dev_pm_opp_find_and_remove_table(struct device *dev)
return;
}
_opp_remove_all_static(opp_table);
/*
* Drop the extra reference only if the OPP table was successfully added
* with dev_pm_opp_of_add_table() earlier.
**/
if (_opp_remove_all_static(opp_table))
dev_pm_opp_put_opp_table(opp_table);
/* Drop reference taken by _find_opp_table() */
dev_pm_opp_put_opp_table(opp_table);
/* Drop reference taken while the OPP table was added */
dev_pm_opp_put_opp_table(opp_table);
}
/**

View File

@ -212,7 +212,7 @@ struct opp_table {
/* Routines internal to opp core */
void dev_pm_opp_get(struct dev_pm_opp *opp);
void _opp_remove_all_static(struct opp_table *opp_table);
bool _opp_remove_all_static(struct opp_table *opp_table);
void _get_opp_table_kref(struct opp_table *opp_table);
int _get_opp_count(struct opp_table *opp_table);
struct opp_table *_find_opp_table(struct device *dev);