45c0594c534b0df2fb36a74b102d30cb74f7e347
[oweals/openwrt.git] / target / linux / brcm2708 / patches-4.19 / 950-0759-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 @@ -881,11 +881,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 @@ -894,7 +892,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 @@ -906,10 +919,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 @@ -949,6 +968,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;