aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-05-31 16:14:44 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-06-02 17:24:41 -0400
commit1aefc75b2449eb68a6fc3ca932e2a4ee353b748d (patch)
treedff2ffcc2d7738bcc93bb968f4d370996c63f9c2 /drivers/cpufreq
parent910c6e881c3a610dfd7575b96975d4df21e3a920 (diff)
cpufreq: stats: Make the stats code non-modular
The modularity of cpufreq_stats is quite problematic. First off, the usage of policy notifiers for the initialization and cleanup in the cpufreq_stats module is inherently racy with respect to CPU offline/online and the initialization and cleanup of the cpufreq driver. Second, fast frequency switching (used by the schedutil governor) cannot be enabled if any transition notifiers are registered, so if the cpufreq_stats module (that registers a transition notifier for updating transition statistics) is loaded, the schedutil governor cannot use fast frequency switching. On the other hand, allowing cpufreq_stats to be built as a module doesn't really add much value. Arguably, there's not much reason for that code to be modular at all. For the above reasons, make the cpufreq stats code non-modular, modify the core to invoke functions provided by that code directly and drop the notifiers from it. Make the stats sysfs attributes appear empty if fast frequency switching is enabled as the statistics will not be updated in that case anyway (and returning -EBUSY from those attributes breaks powertop). While at it, clean up Kconfig help for the CPU_FREQ_STAT and CPU_FREQ_STAT_DETAILS options. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/Kconfig13
-rw-r--r--drivers/cpufreq/cpufreq.c4
-rw-r--r--drivers/cpufreq/cpufreq_stats.c154
3 files changed, 29 insertions, 142 deletions
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index b7445b6ae5a4..c822d72629d5 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -31,23 +31,18 @@ config CPU_FREQ_BOOST_SW
31 depends on THERMAL 31 depends on THERMAL
32 32
33config CPU_FREQ_STAT 33config CPU_FREQ_STAT
34 tristate "CPU frequency translation statistics" 34 bool "CPU frequency transition statistics"
35 default y 35 default y
36 help 36 help
37 This driver exports CPU frequency statistics information through sysfs 37 Export CPU frequency statistics information through sysfs.
38 file system.
39
40 To compile this driver as a module, choose M here: the
41 module will be called cpufreq_stats.
42 38
43 If in doubt, say N. 39 If in doubt, say N.
44 40
45config CPU_FREQ_STAT_DETAILS 41config CPU_FREQ_STAT_DETAILS
46 bool "CPU frequency translation statistics details" 42 bool "CPU frequency transition statistics details"
47 depends on CPU_FREQ_STAT 43 depends on CPU_FREQ_STAT
48 help 44 help
49 This will show detail CPU frequency translation table in sysfs file 45 Show detailed CPU frequency transition table in sysfs.
50 system.
51 46
52 If in doubt, say N. 47 If in doubt, say N.
53 48
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 198416bc22de..c6a14ba239a2 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -347,6 +347,7 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
347 pr_debug("FREQ: %lu - CPU: %lu\n", 347 pr_debug("FREQ: %lu - CPU: %lu\n",
348 (unsigned long)freqs->new, (unsigned long)freqs->cpu); 348 (unsigned long)freqs->new, (unsigned long)freqs->cpu);
349 trace_cpu_frequency(freqs->new, freqs->cpu); 349 trace_cpu_frequency(freqs->new, freqs->cpu);
350 cpufreq_stats_record_transition(policy, freqs->new);
350 srcu_notifier_call_chain(&cpufreq_transition_notifier_list, 351 srcu_notifier_call_chain(&cpufreq_transition_notifier_list,
351 CPUFREQ_POSTCHANGE, freqs); 352 CPUFREQ_POSTCHANGE, freqs);
352 if (likely(policy) && likely(policy->cpu == freqs->cpu)) 353 if (likely(policy) && likely(policy->cpu == freqs->cpu))
@@ -1108,6 +1109,7 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy, bool notify)
1108 CPUFREQ_REMOVE_POLICY, policy); 1109 CPUFREQ_REMOVE_POLICY, policy);
1109 1110
1110 down_write(&policy->rwsem); 1111 down_write(&policy->rwsem);
1112 cpufreq_stats_free_table(policy);
1111 cpufreq_remove_dev_symlink(policy); 1113 cpufreq_remove_dev_symlink(policy);
1112 kobj = &policy->kobj; 1114 kobj = &policy->kobj;
1113 cmp = &policy->kobj_unregister; 1115 cmp = &policy->kobj_unregister;
@@ -1262,6 +1264,8 @@ static int cpufreq_online(unsigned int cpu)
1262 ret = cpufreq_add_dev_interface(policy); 1264 ret = cpufreq_add_dev_interface(policy);
1263 if (ret) 1265 if (ret)
1264 goto out_exit_policy; 1266 goto out_exit_policy;
1267
1268 cpufreq_stats_create_table(policy);
1265 blocking_notifier_call_chain(&cpufreq_policy_notifier_list, 1269 blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
1266 CPUFREQ_CREATE_POLICY, policy); 1270 CPUFREQ_CREATE_POLICY, policy);
1267 1271
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index 5e370a30a964..c6e7f81a0397 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -15,7 +15,7 @@
15#include <linux/slab.h> 15#include <linux/slab.h>
16#include <linux/cputime.h> 16#include <linux/cputime.h>
17 17
18static spinlock_t cpufreq_stats_lock; 18static DEFINE_SPINLOCK(cpufreq_stats_lock);
19 19
20struct cpufreq_stats { 20struct cpufreq_stats {
21 unsigned int total_trans; 21 unsigned int total_trans;
@@ -52,6 +52,9 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
52 ssize_t len = 0; 52 ssize_t len = 0;
53 int i; 53 int i;
54 54
55 if (policy->fast_switch_enabled)
56 return 0;
57
55 cpufreq_stats_update(stats); 58 cpufreq_stats_update(stats);
56 for (i = 0; i < stats->state_num; i++) { 59 for (i = 0; i < stats->state_num; i++) {
57 len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i], 60 len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i],
@@ -68,6 +71,9 @@ static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
68 ssize_t len = 0; 71 ssize_t len = 0;
69 int i, j; 72 int i, j;
70 73
74 if (policy->fast_switch_enabled)
75 return 0;
76
71 len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); 77 len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
72 len += snprintf(buf + len, PAGE_SIZE - len, " : "); 78 len += snprintf(buf + len, PAGE_SIZE - len, " : ");
73 for (i = 0; i < stats->state_num; i++) { 79 for (i = 0; i < stats->state_num; i++) {
@@ -130,7 +136,7 @@ static int freq_table_get_index(struct cpufreq_stats *stats, unsigned int freq)
130 return -1; 136 return -1;
131} 137}
132 138
133static void __cpufreq_stats_free_table(struct cpufreq_policy *policy) 139void cpufreq_stats_free_table(struct cpufreq_policy *policy)
134{ 140{
135 struct cpufreq_stats *stats = policy->stats; 141 struct cpufreq_stats *stats = policy->stats;
136 142
@@ -146,20 +152,7 @@ static void __cpufreq_stats_free_table(struct cpufreq_policy *policy)
146 policy->stats = NULL; 152 policy->stats = NULL;
147} 153}
148 154
149static void cpufreq_stats_free_table(unsigned int cpu) 155void cpufreq_stats_create_table(struct cpufreq_policy *policy)
150{
151 struct cpufreq_policy *policy;
152
153 policy = cpufreq_cpu_get(cpu);
154 if (!policy)
155 return;
156
157 __cpufreq_stats_free_table(policy);
158
159 cpufreq_cpu_put(policy);
160}
161
162static int __cpufreq_stats_create_table(struct cpufreq_policy *policy)
163{ 156{
164 unsigned int i = 0, count = 0, ret = -ENOMEM; 157 unsigned int i = 0, count = 0, ret = -ENOMEM;
165 struct cpufreq_stats *stats; 158 struct cpufreq_stats *stats;
@@ -170,15 +163,15 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy)
170 /* We need cpufreq table for creating stats table */ 163 /* We need cpufreq table for creating stats table */
171 table = cpufreq_frequency_get_table(cpu); 164 table = cpufreq_frequency_get_table(cpu);
172 if (unlikely(!table)) 165 if (unlikely(!table))
173 return 0; 166 return;
174 167
175 /* stats already initialized */ 168 /* stats already initialized */
176 if (policy->stats) 169 if (policy->stats)
177 return -EEXIST; 170 return;
178 171
179 stats = kzalloc(sizeof(*stats), GFP_KERNEL); 172 stats = kzalloc(sizeof(*stats), GFP_KERNEL);
180 if (!stats) 173 if (!stats)
181 return -ENOMEM; 174 return;
182 175
183 /* Find total allocation size */ 176 /* Find total allocation size */
184 cpufreq_for_each_valid_entry(pos, table) 177 cpufreq_for_each_valid_entry(pos, table)
@@ -215,80 +208,32 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy)
215 policy->stats = stats; 208 policy->stats = stats;
216 ret = sysfs_create_group(&policy->kobj, &stats_attr_group); 209 ret = sysfs_create_group(&policy->kobj, &stats_attr_group);
217 if (!ret) 210 if (!ret)
218 return 0; 211 return;
219 212
220 /* We failed, release resources */ 213 /* We failed, release resources */
221 policy->stats = NULL; 214 policy->stats = NULL;
222 kfree(stats->time_in_state); 215 kfree(stats->time_in_state);
223free_stat: 216free_stat:
224 kfree(stats); 217 kfree(stats);
225
226 return ret;
227}
228
229static void cpufreq_stats_create_table(unsigned int cpu)
230{
231 struct cpufreq_policy *policy;
232
233 /*
234 * "likely(!policy)" because normally cpufreq_stats will be registered
235 * before cpufreq driver
236 */
237 policy = cpufreq_cpu_get(cpu);
238 if (likely(!policy))
239 return;
240
241 __cpufreq_stats_create_table(policy);
242
243 cpufreq_cpu_put(policy);
244} 218}
245 219
246static int cpufreq_stat_notifier_policy(struct notifier_block *nb, 220void cpufreq_stats_record_transition(struct cpufreq_policy *policy,
247 unsigned long val, void *data) 221 unsigned int new_freq)
248{ 222{
249 int ret = 0; 223 struct cpufreq_stats *stats = policy->stats;
250 struct cpufreq_policy *policy = data;
251
252 if (val == CPUFREQ_CREATE_POLICY)
253 ret = __cpufreq_stats_create_table(policy);
254 else if (val == CPUFREQ_REMOVE_POLICY)
255 __cpufreq_stats_free_table(policy);
256
257 return ret;
258}
259
260static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
261 unsigned long val, void *data)
262{
263 struct cpufreq_freqs *freq = data;
264 struct cpufreq_policy *policy = cpufreq_cpu_get(freq->cpu);
265 struct cpufreq_stats *stats;
266 int old_index, new_index; 224 int old_index, new_index;
267 225
268 if (!policy) { 226 if (!stats) {
269 pr_err("%s: No policy found\n", __func__);
270 return 0;
271 }
272
273 if (val != CPUFREQ_POSTCHANGE)
274 goto put_policy;
275
276 if (!policy->stats) {
277 pr_debug("%s: No stats found\n", __func__); 227 pr_debug("%s: No stats found\n", __func__);
278 goto put_policy; 228 return;
279 } 229 }
280 230
281 stats = policy->stats;
282
283 old_index = stats->last_index; 231 old_index = stats->last_index;
284 new_index = freq_table_get_index(stats, freq->new); 232 new_index = freq_table_get_index(stats, new_freq);
285 233
286 /* We can't do stats->time_in_state[-1]= .. */ 234 /* We can't do stats->time_in_state[-1]= .. */
287 if (old_index == -1 || new_index == -1) 235 if (old_index == -1 || new_index == -1 || old_index == new_index)
288 goto put_policy; 236 return;
289
290 if (old_index == new_index)
291 goto put_policy;
292 237
293 cpufreq_stats_update(stats); 238 cpufreq_stats_update(stats);
294 239
@@ -297,61 +242,4 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
297 stats->trans_table[old_index * stats->max_state + new_index]++; 242 stats->trans_table[old_index * stats->max_state + new_index]++;
298#endif 243#endif
299 stats->total_trans++; 244 stats->total_trans++;
300
301put_policy:
302 cpufreq_cpu_put(policy);
303 return 0;
304} 245}
305
306static struct notifier_block notifier_policy_block = {
307 .notifier_call = cpufreq_stat_notifier_policy
308};
309
310static struct notifier_block notifier_trans_block = {
311 .notifier_call = cpufreq_stat_notifier_trans
312};
313
314static int __init cpufreq_stats_init(void)
315{
316 int ret;
317 unsigned int cpu;
318
319 spin_lock_init(&cpufreq_stats_lock);
320 ret = cpufreq_register_notifier(&notifier_policy_block,
321 CPUFREQ_POLICY_NOTIFIER);
322 if (ret)
323 return ret;
324
325 for_each_online_cpu(cpu)
326 cpufreq_stats_create_table(cpu);
327
328 ret = cpufreq_register_notifier(&notifier_trans_block,
329 CPUFREQ_TRANSITION_NOTIFIER);
330 if (ret) {
331 cpufreq_unregister_notifier(&notifier_policy_block,
332 CPUFREQ_POLICY_NOTIFIER);
333 for_each_online_cpu(cpu)
334 cpufreq_stats_free_table(cpu);
335 return ret;
336 }
337
338 return 0;
339}
340static void __exit cpufreq_stats_exit(void)
341{
342 unsigned int cpu;
343
344 cpufreq_unregister_notifier(&notifier_policy_block,
345 CPUFREQ_POLICY_NOTIFIER);
346 cpufreq_unregister_notifier(&notifier_trans_block,
347 CPUFREQ_TRANSITION_NOTIFIER);
348 for_each_online_cpu(cpu)
349 cpufreq_stats_free_table(cpu);
350}
351
352MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");
353MODULE_DESCRIPTION("Export cpufreq stats via sysfs");
354MODULE_LICENSE("GPL");
355
356module_init(cpufreq_stats_init);
357module_exit(cpufreq_stats_exit);