acdb00fd48d63a101887d09273b5f9ec6c638983
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-4.19 / 950-0757-cpufreq-scpi-scmi-Fix-freeing-of-dynamic-OPPs.patch
1 From 84a8cbf64731568c0750137ec38091a46b81d502 Mon Sep 17 00:00:00 2001
2 From: Viresh Kumar <viresh.kumar@linaro.org>
3 Date: Fri, 4 Jan 2019 15:14:33 +0530
4 Subject: [PATCH] cpufreq: scpi/scmi: Fix freeing of dynamic OPPs
5
6 Commit 1690d8bb91e370ab772062b79bd434ce815c4729 upstream
7
8 Since the commit 2a4eb7358aba "OPP: Don't remove dynamic OPPs from
9 _dev_pm_opp_remove_table()", dynamically created OPP aren't
10 automatically removed anymore by dev_pm_opp_cpumask_remove_table(). This
11 affects the scpi and scmi cpufreq drivers which no longer free OPPs on
12 failures or on invocations of the policy->exit() callback.
13
14 Create a generic OPP helper dev_pm_opp_remove_all_dynamic() which can be
15 called from these drivers instead of dev_pm_opp_cpumask_remove_table().
16
17 In dev_pm_opp_remove_all_dynamic(), we need to make sure that the
18 opp_list isn't getting accessed simultaneously from other parts of the
19 OPP core while the helper is freeing dynamic OPPs, i.e. we can't drop
20 the opp_table->lock while traversing through the OPP list. And to
21 accomplish that, this patch also creates _opp_kref_release_unlocked()
22 which can be called from this new helper with the opp_table lock already
23 held.
24
25 Cc: 4.20 <stable@vger.kernel.org> # v4.20
26 Reported-by: Valentin Schneider <valentin.schneider@arm.com>
27 Fixes: 2a4eb7358aba "OPP: Don't remove dynamic OPPs from _dev_pm_opp_remove_table()"
28 Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
29 Tested-by: Valentin Schneider <valentin.schneider@arm.com>
30 Reviewed-by: Sudeep Holla <sudeep.holla@arm.com>
31 Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
32 ---
33 drivers/cpufreq/scmi-cpufreq.c | 4 +--
34 drivers/cpufreq/scpi-cpufreq.c | 4 +--
35 drivers/opp/core.c | 63 +++++++++++++++++++++++++++++++---
36 include/linux/pm_opp.h | 5 +++
37 4 files changed, 67 insertions(+), 9 deletions(-)
38
39 --- a/drivers/cpufreq/scmi-cpufreq.c
40 +++ b/drivers/cpufreq/scmi-cpufreq.c
41 @@ -176,7 +176,7 @@ static int scmi_cpufreq_init(struct cpuf
42 out_free_priv:
43 kfree(priv);
44 out_free_opp:
45 - dev_pm_opp_cpumask_remove_table(policy->cpus);
46 + dev_pm_opp_remove_all_dynamic(cpu_dev);
47
48 return ret;
49 }
50 @@ -188,7 +188,7 @@ static int scmi_cpufreq_exit(struct cpuf
51 cpufreq_cooling_unregister(priv->cdev);
52 dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
53 kfree(priv);
54 - dev_pm_opp_cpumask_remove_table(policy->related_cpus);
55 + dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
56
57 return 0;
58 }
59 --- a/drivers/cpufreq/scpi-cpufreq.c
60 +++ b/drivers/cpufreq/scpi-cpufreq.c
61 @@ -177,7 +177,7 @@ out_free_cpufreq_table:
62 out_free_priv:
63 kfree(priv);
64 out_free_opp:
65 - dev_pm_opp_cpumask_remove_table(policy->cpus);
66 + dev_pm_opp_remove_all_dynamic(cpu_dev);
67
68 return ret;
69 }
70 @@ -190,7 +190,7 @@ static int scpi_cpufreq_exit(struct cpuf
71 clk_put(priv->clk);
72 dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
73 kfree(priv);
74 - dev_pm_opp_cpumask_remove_table(policy->related_cpus);
75 + dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
76
77 return 0;
78 }
79 --- a/drivers/opp/core.c
80 +++ b/drivers/opp/core.c
81 @@ -884,11 +884,9 @@ void _opp_free(struct dev_pm_opp *opp)
82 kfree(opp);
83 }
84
85 -static void _opp_kref_release(struct kref *kref)
86 +static void _opp_kref_release(struct dev_pm_opp *opp,
87 + struct opp_table *opp_table)
88 {
89 - struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref);
90 - struct opp_table *opp_table = opp->opp_table;
91 -
92 /*
93 * Notify the changes in the availability of the operable
94 * frequency/voltage list.
95 @@ -897,7 +895,22 @@ static void _opp_kref_release(struct kre
96 opp_debug_remove_one(opp);
97 list_del(&opp->node);
98 kfree(opp);
99 +}
100 +
101 +static void _opp_kref_release_unlocked(struct kref *kref)
102 +{
103 + struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref);
104 + struct opp_table *opp_table = opp->opp_table;
105 +
106 + _opp_kref_release(opp, opp_table);
107 +}
108
109 +static void _opp_kref_release_locked(struct kref *kref)
110 +{
111 + struct dev_pm_opp *opp = container_of(kref, struct dev_pm_opp, kref);
112 + struct opp_table *opp_table = opp->opp_table;
113 +
114 + _opp_kref_release(opp, opp_table);
115 mutex_unlock(&opp_table->lock);
116 dev_pm_opp_put_opp_table(opp_table);
117 }
118 @@ -909,10 +922,16 @@ void dev_pm_opp_get(struct dev_pm_opp *o
119
120 void dev_pm_opp_put(struct dev_pm_opp *opp)
121 {
122 - kref_put_mutex(&opp->kref, _opp_kref_release, &opp->opp_table->lock);
123 + kref_put_mutex(&opp->kref, _opp_kref_release_locked,
124 + &opp->opp_table->lock);
125 }
126 EXPORT_SYMBOL_GPL(dev_pm_opp_put);
127
128 +static void dev_pm_opp_put_unlocked(struct dev_pm_opp *opp)
129 +{
130 + kref_put(&opp->kref, _opp_kref_release_unlocked);
131 +}
132 +
133 /**
134 * dev_pm_opp_remove() - Remove an OPP from OPP table
135 * @dev: device for which we do this operation
136 @@ -952,6 +971,40 @@ void dev_pm_opp_remove(struct device *de
137 }
138 EXPORT_SYMBOL_GPL(dev_pm_opp_remove);
139
140 +/**
141 + * dev_pm_opp_remove_all_dynamic() - Remove all dynamically created OPPs
142 + * @dev: device for which we do this operation
143 + *
144 + * This function removes all dynamically created OPPs from the opp table.
145 + */
146 +void dev_pm_opp_remove_all_dynamic(struct device *dev)
147 +{
148 + struct opp_table *opp_table;
149 + struct dev_pm_opp *opp, *temp;
150 + int count = 0;
151 +
152 + opp_table = _find_opp_table(dev);
153 + if (IS_ERR(opp_table))
154 + return;
155 +
156 + mutex_lock(&opp_table->lock);
157 + list_for_each_entry_safe(opp, temp, &opp_table->opp_list, node) {
158 + if (opp->dynamic) {
159 + dev_pm_opp_put_unlocked(opp);
160 + count++;
161 + }
162 + }
163 + mutex_unlock(&opp_table->lock);
164 +
165 + /* Drop the references taken by dev_pm_opp_add() */
166 + while (count--)
167 + dev_pm_opp_put_opp_table(opp_table);
168 +
169 + /* Drop the reference taken by _find_opp_table() */
170 + dev_pm_opp_put_opp_table(opp_table);
171 +}
172 +EXPORT_SYMBOL_GPL(dev_pm_opp_remove_all_dynamic);
173 +
174 struct dev_pm_opp *_opp_allocate(struct opp_table *table)
175 {
176 struct dev_pm_opp *opp;
177 --- a/include/linux/pm_opp.h
178 +++ b/include/linux/pm_opp.h
179 @@ -107,6 +107,7 @@ void dev_pm_opp_put(struct dev_pm_opp *o
180 int dev_pm_opp_add(struct device *dev, unsigned long freq,
181 unsigned long u_volt);
182 void dev_pm_opp_remove(struct device *dev, unsigned long freq);
183 +void dev_pm_opp_remove_all_dynamic(struct device *dev);
184
185 int dev_pm_opp_enable(struct device *dev, unsigned long freq);
186
187 @@ -208,6 +209,10 @@ static inline void dev_pm_opp_remove(str
188 {
189 }
190
191 +static inline void dev_pm_opp_remove_all_dynamic(struct device *dev)
192 +{
193 +}
194 +
195 static inline int dev_pm_opp_enable(struct device *dev, unsigned long freq)
196 {
197 return 0;