Skip to content

Commit

Permalink
OPP: Create separate kref for static OPPs list
Browse files Browse the repository at this point in the history
The static OPPs don't always get freed with the OPP table, it can happen
before that as well. For example, if the OPP table is first created
using helpers like dev_pm_opp_set_supported_hw() and the OPPs are
created at a later point. Now when the OPPs are removed, the OPP table
stays until the time dev_pm_opp_put_supported_hw() is called.

Later patches will streamline the freeing of OPP table and that requires
the static OPPs to get freed with help of a separate kernel reference.
This patch prepares for that by creating a separate kref for static OPPs
list.

Tested-by: Niklas Cassel <[email protected]>
Signed-off-by: Viresh Kumar <[email protected]>
  • Loading branch information
vireshk committed Sep 19, 2018
1 parent 0ad8c62 commit d0e8ae6
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 1 deletion.
33 changes: 32 additions & 1 deletion drivers/opp/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,33 @@ static void _opp_table_kref_release(struct kref *kref)
mutex_unlock(&opp_table_lock);
}

void _opp_remove_all_static(struct opp_table *opp_table)
{
struct dev_pm_opp *opp, *tmp;

list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) {
if (!opp->dynamic)
dev_pm_opp_put(opp);
}

opp_table->parsed_static_opps = false;
}

static void _opp_table_list_kref_release(struct kref *kref)
{
struct opp_table *opp_table = container_of(kref, struct opp_table,
list_kref);

_opp_remove_all_static(opp_table);
mutex_unlock(&opp_table_lock);
}

void _put_opp_list_kref(struct opp_table *opp_table)
{
kref_put_mutex(&opp_table->list_kref, _opp_table_list_kref_release,
&opp_table_lock);
}

void dev_pm_opp_put_opp_table(struct opp_table *opp_table)
{
kref_put_mutex(&opp_table->kref, _opp_table_kref_release,
Expand Down Expand Up @@ -1746,8 +1773,11 @@ void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev,
/* Find if opp_table manages a single device */
if (list_is_singular(&opp_table->dev_list)) {
/* Free static OPPs */
_put_opp_list_kref(opp_table);

/* Free dynamic OPPs */
list_for_each_entry_safe(opp, tmp, &opp_table->opp_list, node) {
if (remove_all || !opp->dynamic)
if (remove_all)
dev_pm_opp_put(opp);
}

Expand All @@ -1758,6 +1788,7 @@ void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev,
if (opp_table->genpd_performance_state)
dev_pm_genpd_set_performance_state(dev, 0);
} else {
_put_opp_list_kref(opp_table);
_remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table);
}

Expand Down
7 changes: 7 additions & 0 deletions drivers/opp/of.c
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,8 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np,
ret = -ENOMEM;
else if (!opp_table->parsed_static_opps)
goto initialize_static_opps;
else
kref_get(&opp_table->list_kref);

goto put_opp_table;
}
Expand All @@ -420,6 +422,8 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np,
return -ENOMEM;

initialize_static_opps:
kref_init(&opp_table->list_kref);

/* We have opp-table node now, iterate over it and add OPPs */
for_each_available_child_of_node(opp_np, np) {
count++;
Expand All @@ -437,6 +441,7 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np,
/* There should be one of more OPP defined */
if (WARN_ON(!count)) {
ret = -ENOENT;
_put_opp_list_kref(opp_table);
goto put_opp_table;
}

Expand Down Expand Up @@ -491,6 +496,8 @@ static int _of_add_opp_table_v1(struct device *dev)
if (!opp_table)
return -ENOMEM;

kref_init(&opp_table->list_kref);

val = prop->value;
while (nr) {
unsigned long freq = be32_to_cpup(val++) * 1000;
Expand Down
3 changes: 3 additions & 0 deletions drivers/opp/opp.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ enum opp_table_access {
* @dev_list: list of devices that share these OPPs
* @opp_list: table of opps
* @kref: for reference count of the table.
* @list_kref: for reference count of the OPP list.
* @lock: mutex protecting the opp_list and dev_list.
* @np: struct device_node pointer for opp's DT node.
* @clock_latency_ns_max: Max clock latency in nanoseconds.
Expand Down Expand Up @@ -157,6 +158,7 @@ struct opp_table {
struct list_head dev_list;
struct list_head opp_list;
struct kref kref;
struct kref list_kref;
struct mutex lock;

struct device_node *np;
Expand Down Expand Up @@ -200,6 +202,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *o
int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic);
void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, bool of, int last_cpu);
struct opp_table *_add_opp_table(struct device *dev);
void _put_opp_list_kref(struct opp_table *opp_table);

#ifdef CONFIG_OF
void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index);
Expand Down

0 comments on commit d0e8ae6

Please sign in to comment.