Skip to content

Commit 1690d8b

Browse files
vireshkrafaeljw
authored andcommitted
cpufreq: scpi/scmi: Fix freeing of dynamic OPPs
Since the commit 2a4eb73 "OPP: Don't remove dynamic OPPs from _dev_pm_opp_remove_table()", dynamically created OPP aren't automatically removed anymore by dev_pm_opp_cpumask_remove_table(). This affects the scpi and scmi cpufreq drivers which no longer free OPPs on failures or on invocations of the policy->exit() callback. Create a generic OPP helper dev_pm_opp_remove_all_dynamic() which can be called from these drivers instead of dev_pm_opp_cpumask_remove_table(). In dev_pm_opp_remove_all_dynamic(), we need to make sure that the opp_list isn't getting accessed simultaneously from other parts of the OPP core while the helper is freeing dynamic OPPs, i.e. we can't drop the opp_table->lock while traversing through the OPP list. And to accomplish that, this patch also creates _opp_kref_release_unlocked() which can be called from this new helper with the opp_table lock already held. Cc: 4.20 <stable@vger.kernel.org> # v4.20 Reported-by: Valentin Schneider <valentin.schneider@arm.com> Fixes: 2a4eb73 "OPP: Don't remove dynamic OPPs from _dev_pm_opp_remove_table()" Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Valentin Schneider <valentin.schneider@arm.com> Reviewed-by: Sudeep Holla <sudeep.holla@arm.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent 088d923 commit 1690d8b

File tree

4 files changed

+67
-9
lines changed

4 files changed

+67
-9
lines changed

drivers/cpufreq/scmi-cpufreq.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
176176
out_free_priv:
177177
kfree(priv);
178178
out_free_opp:
179-
dev_pm_opp_cpumask_remove_table(policy->cpus);
179+
dev_pm_opp_remove_all_dynamic(cpu_dev);
180180

181181
return ret;
182182
}
@@ -188,7 +188,7 @@ static int scmi_cpufreq_exit(struct cpufreq_policy *policy)
188188
cpufreq_cooling_unregister(priv->cdev);
189189
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
190190
kfree(priv);
191-
dev_pm_opp_cpumask_remove_table(policy->related_cpus);
191+
dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
192192

193193
return 0;
194194
}

drivers/cpufreq/scpi-cpufreq.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ static int scpi_cpufreq_init(struct cpufreq_policy *policy)
177177
out_free_priv:
178178
kfree(priv);
179179
out_free_opp:
180-
dev_pm_opp_cpumask_remove_table(policy->cpus);
180+
dev_pm_opp_remove_all_dynamic(cpu_dev);
181181

182182
return ret;
183183
}
@@ -190,7 +190,7 @@ static int scpi_cpufreq_exit(struct cpufreq_policy *policy)
190190
clk_put(priv->clk);
191191
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
192192
kfree(priv);
193-
dev_pm_opp_cpumask_remove_table(policy->related_cpus);
193+
dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
194194

195195
return 0;
196196
}

drivers/opp/core.c

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -988,11 +988,9 @@ void _opp_free(struct dev_pm_opp *opp)
988988
kfree(opp);
989989
}
990990

991-
static void _opp_kref_release(struct kref *kref)
991+
static void _opp_kref_release(struct dev_pm_opp *opp,
992+
struct opp_table *opp_table)
992993
{
993-
struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref);
994-
struct opp_table *opp_table = opp->opp_table;
995-
996994
/*
997995
* Notify the changes in the availability of the operable
998996
* frequency/voltage list.
@@ -1002,7 +1000,22 @@ static void _opp_kref_release(struct kref *kref)
10021000
opp_debug_remove_one(opp);
10031001
list_del(&opp->node);
10041002
kfree(opp);
1003+
}
10051004

1005+
static void _opp_kref_release_unlocked(struct kref *kref)
1006+
{
1007+
struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref);
1008+
struct opp_table *opp_table = opp->opp_table;
1009+
1010+
_opp_kref_release(opp, opp_table);
1011+
}
1012+
1013+
static void _opp_kref_release_locked(struct kref *kref)
1014+
{
1015+
struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref);
1016+
struct opp_table *opp_table = opp->opp_table;
1017+
1018+
_opp_kref_release(opp, opp_table);
10061019
mutex_unlock(&opp_table->lock);
10071020
}
10081021

@@ -1013,10 +1026,16 @@ void dev_pm_opp_get(struct dev_pm_opp *opp)
10131026

10141027
void dev_pm_opp_put(struct dev_pm_opp *opp)
10151028
{
1016-
kref_put_mutex(&opp->kref, _opp_kref_release, &opp->opp_table->lock);
1029+
kref_put_mutex(&opp->kref, _opp_kref_release_locked,
1030+
&opp->opp_table->lock);
10171031
}
10181032
EXPORT_SYMBOL_GPL(dev_pm_opp_put);
10191033

1034+
static void dev_pm_opp_put_unlocked(struct dev_pm_opp *opp)
1035+
{
1036+
kref_put(&opp->kref, _opp_kref_release_unlocked);
1037+
}
1038+
10201039
/**
10211040
* dev_pm_opp_remove() - Remove an OPP from OPP table
10221041
* @dev: device for which we do this operation
@@ -1060,6 +1079,40 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq)
10601079
}
10611080
EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
10621081

1082+
/**
1083+
* dev_pm_opp_remove_all_dynamic() - Remove all dynamically created OPPs
1084+
* @dev: device for which we do this operation
1085+
*
1086+
* This function removes all dynamically created OPPs from the opp table.
1087+
*/
1088+
void dev_pm_opp_remove_all_dynamic(struct device *dev)
1089+
{
1090+
struct opp_table *opp_table;
1091+
struct dev_pm_opp *opp, *temp;
1092+
int count = 0;
1093+
1094+
opp_table = _find_opp_table(dev);
1095+
if (IS_ERR(opp_table))
1096+
return;
1097+
1098+
mutex_lock(&opp_table->lock);
1099+
list_for_each_entry_safe(opp, temp, &opp_table->opp_list, node) {
1100+
if (opp->dynamic) {
1101+
dev_pm_opp_put_unlocked(opp);
1102+
count++;
1103+
}
1104+
}
1105+
mutex_unlock(&opp_table->lock);
1106+
1107+
/* Drop the references taken by dev_pm_opp_add() */
1108+
while (count--)
1109+
dev_pm_opp_put_opp_table(opp_table);
1110+
1111+
/* Drop the reference taken by _find_opp_table() */
1112+
dev_pm_opp_put_opp_table(opp_table);
1113+
}
1114+
EXPORT_SYMBOL_GPL(dev_pm_opp_remove_all_dynamic);
1115+
10631116
struct dev_pm_opp *_opp_allocate(struct opp_table *table)
10641117
{
10651118
struct dev_pm_opp *opp;

include/linux/pm_opp.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ void dev_pm_opp_put(struct dev_pm_opp *opp);
108108
int dev_pm_opp_add(struct device *dev, unsigned long freq,
109109
unsigned long u_volt);
110110
void dev_pm_opp_remove(struct device *dev, unsigned long freq);
111+
void dev_pm_opp_remove_all_dynamic(struct device *dev);
111112

112113
int dev_pm_opp_enable(struct device *dev, unsigned long freq);
113114

@@ -217,6 +218,10 @@ static inline void dev_pm_opp_remove(struct device *dev, unsigned long freq)
217218
{
218219
}
219220

221+
static inline void dev_pm_opp_remove_all_dynamic(struct device *dev)
222+
{
223+
}
224+
220225
static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
221226
{
222227
return 0;

0 commit comments

Comments
 (0)