| From foo@baz Sat Nov 10 11:24:34 PST 2018 |
| From: Viresh Kumar <viresh.kumar@linaro.org> |
| Date: Wed, 3 Oct 2018 15:35:21 +0530 |
| Subject: cpufreq: dt: Try freeing static OPPs only if we have added them |
| |
| From: Viresh Kumar <viresh.kumar@linaro.org> |
| |
| [ Upstream commit 51c99dd2c06b234575661fa1e0a1dea6c3ef566f ] |
| |
| We can not call dev_pm_opp_of_cpumask_remove_table() freely anymore |
| since the latest OPP core updates as that uses reference counting to |
| free resources. There are cases where no static OPPs are added (using |
| DT) for a platform and trying to remove the OPP table may end up |
| decrementing refcount which is already zero and hence generating |
| warnings. |
| |
| Lets track if we were able to add static OPPs or not and then only |
| remove the table based on that. Some reshuffling of code is also done to |
| do that. |
| |
| Reported-by: Niklas Cassel <niklas.cassel@linaro.org> |
| Tested-by: Niklas Cassel <niklas.cassel@linaro.org> |
| Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/cpufreq/cpufreq-dt.c | 34 +++++++++++++++++++--------------- |
| 1 file changed, 19 insertions(+), 15 deletions(-) |
| |
| --- a/drivers/cpufreq/cpufreq-dt.c |
| +++ b/drivers/cpufreq/cpufreq-dt.c |
| @@ -32,6 +32,7 @@ struct private_data { |
| struct device *cpu_dev; |
| struct thermal_cooling_device *cdev; |
| const char *reg_name; |
| + bool have_static_opps; |
| }; |
| |
| static struct freq_attr *cpufreq_dt_attr[] = { |
| @@ -197,6 +198,15 @@ static int cpufreq_init(struct cpufreq_p |
| } |
| } |
| |
| + priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
| + if (!priv) { |
| + ret = -ENOMEM; |
| + goto out_put_regulator; |
| + } |
| + |
| + priv->reg_name = name; |
| + priv->opp_table = opp_table; |
| + |
| /* |
| * Initialize OPP tables for all policy->cpus. They will be shared by |
| * all CPUs which have marked their CPUs shared with OPP bindings. |
| @@ -207,7 +217,8 @@ static int cpufreq_init(struct cpufreq_p |
| * |
| * OPPs might be populated at runtime, don't check for error here |
| */ |
| - dev_pm_opp_of_cpumask_add_table(policy->cpus); |
| + if (!dev_pm_opp_of_cpumask_add_table(policy->cpus)) |
| + priv->have_static_opps = true; |
| |
| /* |
| * But we need OPP table to function so if it is not there let's |
| @@ -233,19 +244,10 @@ static int cpufreq_init(struct cpufreq_p |
| __func__, ret); |
| } |
| |
| - priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
| - if (!priv) { |
| - ret = -ENOMEM; |
| - goto out_free_opp; |
| - } |
| - |
| - priv->reg_name = name; |
| - priv->opp_table = opp_table; |
| - |
| ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); |
| if (ret) { |
| dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); |
| - goto out_free_priv; |
| + goto out_free_opp; |
| } |
| |
| priv->cpu_dev = cpu_dev; |
| @@ -284,10 +286,11 @@ static int cpufreq_init(struct cpufreq_p |
| |
| out_free_cpufreq_table: |
| dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); |
| -out_free_priv: |
| - kfree(priv); |
| out_free_opp: |
| - dev_pm_opp_of_cpumask_remove_table(policy->cpus); |
| + if (priv->have_static_opps) |
| + dev_pm_opp_of_cpumask_remove_table(policy->cpus); |
| + kfree(priv); |
| +out_put_regulator: |
| if (name) |
| dev_pm_opp_put_regulator(opp_table); |
| out_put_clk: |
| @@ -302,7 +305,8 @@ static int cpufreq_exit(struct cpufreq_p |
| |
| cpufreq_cooling_unregister(priv->cdev); |
| dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); |
| - dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); |
| + if (priv->have_static_opps) |
| + dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); |
| if (priv->reg_name) |
| dev_pm_opp_put_regulator(priv->opp_table); |
| |