1 // SPDX-License-Identifier: GPL-2.0-only
3 * drivers/cpufreq/cpufreq_stats.c
5 * Copyright (C) 2003-2004 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
6 * (C) 2004 Zou Nan hai <nanhai.zou@intel.com>.
10 #include <linux/cpufreq.h>
11 #include <linux/module.h>
12 #include <linux/sched/clock.h>
13 #include <linux/slab.h>
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
;
22 unsigned int *freq_table
;
23 unsigned int *trans_table
;
26 unsigned int reset_pending
;
27 unsigned long long reset_time
;
30 static void cpufreq_stats_update(struct cpufreq_stats
*stats
,
31 unsigned long long time
)
33 unsigned long long cur_time
= local_clock();
35 stats
->time_in_state
[stats
->last_index
] += cur_time
- time
;
36 stats
->last_time
= cur_time
;
39 static void cpufreq_stats_reset_table(struct cpufreq_stats
*stats
)
41 unsigned int count
= stats
->max_state
;
43 memset(stats
->time_in_state
, 0, count
* sizeof(u64
));
44 memset(stats
->trans_table
, 0, count
* count
* sizeof(int));
45 stats
->last_time
= local_clock();
46 stats
->total_trans
= 0;
48 /* Adjust for the time elapsed since reset was requested */
49 WRITE_ONCE(stats
->reset_pending
, 0);
51 * Prevent the reset_time read from being reordered before the
52 * reset_pending accesses in cpufreq_stats_record_transition().
55 cpufreq_stats_update(stats
, READ_ONCE(stats
->reset_time
));
58 static ssize_t
show_total_trans(struct cpufreq_policy
*policy
, char *buf
)
60 struct cpufreq_stats
*stats
= policy
->stats
;
62 if (READ_ONCE(stats
->reset_pending
))
63 return sprintf(buf
, "%d\n", 0);
65 return sprintf(buf
, "%u\n", stats
->total_trans
);
67 cpufreq_freq_attr_ro(total_trans
);
69 static ssize_t
show_time_in_state(struct cpufreq_policy
*policy
, char *buf
)
71 struct cpufreq_stats
*stats
= policy
->stats
;
72 bool pending
= READ_ONCE(stats
->reset_pending
);
73 unsigned long long time
;
77 for (i
= 0; i
< stats
->state_num
; i
++) {
79 if (i
== stats
->last_index
) {
81 * Prevent the reset_time read from occurring
82 * before the reset_pending read above.
85 time
= local_clock() - READ_ONCE(stats
->reset_time
);
90 time
= stats
->time_in_state
[i
];
91 if (i
== stats
->last_index
)
92 time
+= local_clock() - stats
->last_time
;
95 len
+= sprintf(buf
+ len
, "%u %llu\n", stats
->freq_table
[i
],
96 nsec_to_clock_t(time
));
100 cpufreq_freq_attr_ro(time_in_state
);
102 /* We don't care what is written to the attribute */
103 static ssize_t
store_reset(struct cpufreq_policy
*policy
, const char *buf
,
106 struct cpufreq_stats
*stats
= policy
->stats
;
109 * Defer resetting of stats to cpufreq_stats_record_transition() to
112 WRITE_ONCE(stats
->reset_time
, local_clock());
114 * The memory barrier below is to prevent the readers of reset_time from
115 * seeing a stale or partially updated value.
118 WRITE_ONCE(stats
->reset_pending
, 1);
122 cpufreq_freq_attr_wo(reset
);
124 static ssize_t
show_trans_table(struct cpufreq_policy
*policy
, char *buf
)
126 struct cpufreq_stats
*stats
= policy
->stats
;
127 bool pending
= READ_ONCE(stats
->reset_pending
);
131 len
+= sysfs_emit_at(buf
, len
, " From : To\n");
132 len
+= sysfs_emit_at(buf
, len
, " : ");
133 for (i
= 0; i
< stats
->state_num
; i
++) {
134 if (len
>= PAGE_SIZE
- 1)
136 len
+= sysfs_emit_at(buf
, len
, "%9u ", stats
->freq_table
[i
]);
138 if (len
>= PAGE_SIZE
- 1)
139 return PAGE_SIZE
- 1;
141 len
+= sysfs_emit_at(buf
, len
, "\n");
143 for (i
= 0; i
< stats
->state_num
; i
++) {
144 if (len
>= PAGE_SIZE
- 1)
147 len
+= sysfs_emit_at(buf
, len
, "%9u: ", stats
->freq_table
[i
]);
149 for (j
= 0; j
< stats
->state_num
; j
++) {
150 if (len
>= PAGE_SIZE
- 1)
156 count
= stats
->trans_table
[i
* stats
->max_state
+ j
];
158 len
+= sysfs_emit_at(buf
, len
, "%9u ", count
);
160 if (len
>= PAGE_SIZE
- 1)
162 len
+= sysfs_emit_at(buf
, len
, "\n");
165 if (len
>= PAGE_SIZE
- 1) {
166 pr_warn_once("cpufreq transition table exceeds PAGE_SIZE. Disabling\n");
171 cpufreq_freq_attr_ro(trans_table
);
173 static struct attribute
*default_attrs
[] = {
180 static const struct attribute_group stats_attr_group
= {
181 .attrs
= default_attrs
,
185 static int freq_table_get_index(struct cpufreq_stats
*stats
, unsigned int freq
)
188 for (index
= 0; index
< stats
->max_state
; index
++)
189 if (stats
->freq_table
[index
] == freq
)
194 void cpufreq_stats_free_table(struct cpufreq_policy
*policy
)
196 struct cpufreq_stats
*stats
= policy
->stats
;
202 pr_debug("%s: Free stats table\n", __func__
);
204 sysfs_remove_group(&policy
->kobj
, &stats_attr_group
);
205 kfree(stats
->time_in_state
);
207 policy
->stats
= NULL
;
210 void cpufreq_stats_create_table(struct cpufreq_policy
*policy
)
212 unsigned int i
= 0, count
;
213 struct cpufreq_stats
*stats
;
214 unsigned int alloc_size
;
215 struct cpufreq_frequency_table
*pos
;
217 count
= cpufreq_table_count_valid_entries(policy
);
221 /* stats already initialized */
225 stats
= kzalloc(sizeof(*stats
), GFP_KERNEL
);
229 alloc_size
= count
* sizeof(int) + count
* sizeof(u64
);
231 alloc_size
+= count
* count
* sizeof(int);
233 /* Allocate memory for time_in_state/freq_table/trans_table in one go */
234 stats
->time_in_state
= kzalloc(alloc_size
, GFP_KERNEL
);
235 if (!stats
->time_in_state
)
238 stats
->freq_table
= (unsigned int *)(stats
->time_in_state
+ count
);
240 stats
->trans_table
= stats
->freq_table
+ count
;
242 stats
->max_state
= count
;
244 /* Find valid-unique entries */
245 cpufreq_for_each_valid_entry(pos
, policy
->freq_table
)
246 if (policy
->freq_table_sorted
!= CPUFREQ_TABLE_UNSORTED
||
247 freq_table_get_index(stats
, pos
->frequency
) == -1)
248 stats
->freq_table
[i
++] = pos
->frequency
;
250 stats
->state_num
= i
;
251 stats
->last_time
= local_clock();
252 stats
->last_index
= freq_table_get_index(stats
, policy
->cur
);
254 policy
->stats
= stats
;
255 if (!sysfs_create_group(&policy
->kobj
, &stats_attr_group
))
258 /* We failed, release resources */
259 policy
->stats
= NULL
;
260 kfree(stats
->time_in_state
);
265 void cpufreq_stats_record_transition(struct cpufreq_policy
*policy
,
266 unsigned int new_freq
)
268 struct cpufreq_stats
*stats
= policy
->stats
;
269 int old_index
, new_index
;
271 if (unlikely(!stats
))
274 if (unlikely(READ_ONCE(stats
->reset_pending
)))
275 cpufreq_stats_reset_table(stats
);
277 old_index
= stats
->last_index
;
278 new_index
= freq_table_get_index(stats
, new_freq
);
280 /* We can't do stats->time_in_state[-1]= .. */
281 if (unlikely(old_index
== -1 || new_index
== -1 || old_index
== new_index
))
284 cpufreq_stats_update(stats
, stats
->last_time
);
286 stats
->last_index
= new_index
;
287 stats
->trans_table
[old_index
* stats
->max_state
+ new_index
]++;
288 stats
->total_trans
++;