cpufreq: stats: Mark few conditionals with unlikely()
[linux-2.6-microblaze.git] / drivers / cpufreq / cpufreq_stats.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  drivers/cpufreq/cpufreq_stats.c
4  *
5  *  Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
6  *  (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
7  */
8
9 #include <linux/cpu.h>
10 #include <linux/cpufreq.h>
11 #include <linux/module.h>
12 #include <linux/slab.h>
13
14
15 struct cpufreq_stats {
16         unsigned int total_trans;
17         unsigned long long last_time;
18         unsigned int max_state;
19         unsigned int state_num;
20         unsigned int last_index;
21         u64 *time_in_state;
22         unsigned int *freq_table;
23         unsigned int *trans_table;
24
25         /* Deferred reset */
26         unsigned int reset_pending;
27         unsigned long long reset_time;
28 };
29
30 static void cpufreq_stats_update(struct cpufreq_stats *stats,
31                                  unsigned long long time)
32 {
33         unsigned long long cur_time = get_jiffies_64();
34
35         stats->time_in_state[stats->last_index] += cur_time - time;
36         stats->last_time = cur_time;
37 }
38
39 static void cpufreq_stats_reset_table(struct cpufreq_stats *stats)
40 {
41         unsigned int count = stats->max_state;
42
43         memset(stats->time_in_state, 0, count * sizeof(u64));
44         memset(stats->trans_table, 0, count * count * sizeof(int));
45         stats->last_time = get_jiffies_64();
46         stats->total_trans = 0;
47
48         /* Adjust for the time elapsed since reset was requested */
49         WRITE_ONCE(stats->reset_pending, 0);
50         cpufreq_stats_update(stats, READ_ONCE(stats->reset_time));
51 }
52
53 static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
54 {
55         struct cpufreq_stats *stats = policy->stats;
56
57         if (READ_ONCE(stats->reset_pending))
58                 return sprintf(buf, "%d\n", 0);
59         else
60                 return sprintf(buf, "%d\n", stats->total_trans);
61 }
62 cpufreq_freq_attr_ro(total_trans);
63
64 static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
65 {
66         struct cpufreq_stats *stats = policy->stats;
67         bool pending = READ_ONCE(stats->reset_pending);
68         unsigned long long time;
69         ssize_t len = 0;
70         int i;
71
72         if (policy->fast_switch_enabled)
73                 return 0;
74
75         for (i = 0; i < stats->state_num; i++) {
76                 if (pending) {
77                         if (i == stats->last_index)
78                                 time = get_jiffies_64() - READ_ONCE(stats->reset_time);
79                         else
80                                 time = 0;
81                 } else {
82                         time = stats->time_in_state[i];
83                         if (i == stats->last_index)
84                                 time += get_jiffies_64() - stats->last_time;
85                 }
86
87                 len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i],
88                                jiffies_64_to_clock_t(time));
89         }
90         return len;
91 }
92 cpufreq_freq_attr_ro(time_in_state);
93
94 /* We don't care what is written to the attribute */
95 static ssize_t store_reset(struct cpufreq_policy *policy, const char *buf,
96                            size_t count)
97 {
98         struct cpufreq_stats *stats = policy->stats;
99
100         /*
101          * Defer resetting of stats to cpufreq_stats_record_transition() to
102          * avoid races.
103          */
104         WRITE_ONCE(stats->reset_time, get_jiffies_64());
105         WRITE_ONCE(stats->reset_pending, 1);
106
107         return count;
108 }
109 cpufreq_freq_attr_wo(reset);
110
111 static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
112 {
113         struct cpufreq_stats *stats = policy->stats;
114         bool pending = READ_ONCE(stats->reset_pending);
115         ssize_t len = 0;
116         int i, j, count;
117
118         if (policy->fast_switch_enabled)
119                 return 0;
120
121         len += scnprintf(buf + len, PAGE_SIZE - len, "   From  :    To\n");
122         len += scnprintf(buf + len, PAGE_SIZE - len, "         : ");
123         for (i = 0; i < stats->state_num; i++) {
124                 if (len >= PAGE_SIZE)
125                         break;
126                 len += scnprintf(buf + len, PAGE_SIZE - len, "%9u ",
127                                 stats->freq_table[i]);
128         }
129         if (len >= PAGE_SIZE)
130                 return PAGE_SIZE;
131
132         len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
133
134         for (i = 0; i < stats->state_num; i++) {
135                 if (len >= PAGE_SIZE)
136                         break;
137
138                 len += scnprintf(buf + len, PAGE_SIZE - len, "%9u: ",
139                                 stats->freq_table[i]);
140
141                 for (j = 0; j < stats->state_num; j++) {
142                         if (len >= PAGE_SIZE)
143                                 break;
144
145                         if (pending)
146                                 count = 0;
147                         else
148                                 count = stats->trans_table[i * stats->max_state + j];
149
150                         len += scnprintf(buf + len, PAGE_SIZE - len, "%9u ", count);
151                 }
152                 if (len >= PAGE_SIZE)
153                         break;
154                 len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
155         }
156
157         if (len >= PAGE_SIZE) {
158                 pr_warn_once("cpufreq transition table exceeds PAGE_SIZE. Disabling\n");
159                 return -EFBIG;
160         }
161         return len;
162 }
163 cpufreq_freq_attr_ro(trans_table);
164
165 static struct attribute *default_attrs[] = {
166         &total_trans.attr,
167         &time_in_state.attr,
168         &reset.attr,
169         &trans_table.attr,
170         NULL
171 };
172 static const struct attribute_group stats_attr_group = {
173         .attrs = default_attrs,
174         .name = "stats"
175 };
176
177 static int freq_table_get_index(struct cpufreq_stats *stats, unsigned int freq)
178 {
179         int index;
180         for (index = 0; index < stats->max_state; index++)
181                 if (stats->freq_table[index] == freq)
182                         return index;
183         return -1;
184 }
185
186 void cpufreq_stats_free_table(struct cpufreq_policy *policy)
187 {
188         struct cpufreq_stats *stats = policy->stats;
189
190         /* Already freed */
191         if (!stats)
192                 return;
193
194         pr_debug("%s: Free stats table\n", __func__);
195
196         sysfs_remove_group(&policy->kobj, &stats_attr_group);
197         kfree(stats->time_in_state);
198         kfree(stats);
199         policy->stats = NULL;
200 }
201
202 void cpufreq_stats_create_table(struct cpufreq_policy *policy)
203 {
204         unsigned int i = 0, count = 0, ret = -ENOMEM;
205         struct cpufreq_stats *stats;
206         unsigned int alloc_size;
207         struct cpufreq_frequency_table *pos;
208
209         count = cpufreq_table_count_valid_entries(policy);
210         if (!count)
211                 return;
212
213         /* stats already initialized */
214         if (policy->stats)
215                 return;
216
217         stats = kzalloc(sizeof(*stats), GFP_KERNEL);
218         if (!stats)
219                 return;
220
221         alloc_size = count * sizeof(int) + count * sizeof(u64);
222
223         alloc_size += count * count * sizeof(int);
224
225         /* Allocate memory for time_in_state/freq_table/trans_table in one go */
226         stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
227         if (!stats->time_in_state)
228                 goto free_stat;
229
230         stats->freq_table = (unsigned int *)(stats->time_in_state + count);
231
232         stats->trans_table = stats->freq_table + count;
233
234         stats->max_state = count;
235
236         /* Find valid-unique entries */
237         cpufreq_for_each_valid_entry(pos, policy->freq_table)
238                 if (freq_table_get_index(stats, pos->frequency) == -1)
239                         stats->freq_table[i++] = pos->frequency;
240
241         stats->state_num = i;
242         stats->last_time = get_jiffies_64();
243         stats->last_index = freq_table_get_index(stats, policy->cur);
244
245         policy->stats = stats;
246         ret = sysfs_create_group(&policy->kobj, &stats_attr_group);
247         if (!ret)
248                 return;
249
250         /* We failed, release resources */
251         policy->stats = NULL;
252         kfree(stats->time_in_state);
253 free_stat:
254         kfree(stats);
255 }
256
257 void cpufreq_stats_record_transition(struct cpufreq_policy *policy,
258                                      unsigned int new_freq)
259 {
260         struct cpufreq_stats *stats = policy->stats;
261         int old_index, new_index;
262
263         if (unlikely(!stats))
264                 return;
265
266         if (unlikely(READ_ONCE(stats->reset_pending)))
267                 cpufreq_stats_reset_table(stats);
268
269         old_index = stats->last_index;
270         new_index = freq_table_get_index(stats, new_freq);
271
272         /* We can't do stats->time_in_state[-1]= .. */
273         if (unlikely(old_index == -1 || new_index == -1 || old_index == new_index))
274                 return;
275
276         cpufreq_stats_update(stats, stats->last_time);
277
278         stats->last_index = new_index;
279         stats->trans_table[old_index * stats->max_state + new_index]++;
280         stats->total_trans++;
281 }