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/sched/clock.h> 13 #include <linux/slab.h> 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 = local_clock(); 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 = local_clock(); 46 stats->total_trans = 0; 47 48 /* Adjust for the time elapsed since reset was requested */ 49 WRITE_ONCE(stats->reset_pending, 0); 50 /* 51 * Prevent the reset_time read from being reordered before the 52 * reset_pending accesses in cpufreq_stats_record_transition(). 53 */ 54 smp_rmb(); 55 cpufreq_stats_update(stats, READ_ONCE(stats->reset_time)); 56 } 57 58 static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf) 59 { 60 struct cpufreq_stats *stats = policy->stats; 61 62 if (READ_ONCE(stats->reset_pending)) 63 return sprintf(buf, "%d\n", 0); 64 else 65 return sprintf(buf, "%u\n", stats->total_trans); 66 } 67 cpufreq_freq_attr_ro(total_trans); 68 69 static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf) 70 { 71 struct cpufreq_stats *stats = policy->stats; 72 bool pending = READ_ONCE(stats->reset_pending); 73 unsigned long long time; 74 ssize_t len = 0; 75 int i; 76 77 for (i = 0; i < stats->state_num; i++) { 78 if (pending) { 79 if (i == stats->last_index) { 80 /* 81 * Prevent the reset_time read from occurring 82 * before the reset_pending read above. 83 */ 84 smp_rmb(); 85 time = local_clock() - READ_ONCE(stats->reset_time); 86 } else { 87 time = 0; 88 } 89 } else { 90 time = stats->time_in_state[i]; 91 if (i == stats->last_index) 92 time += local_clock() - stats->last_time; 93 } 94 95 len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i], 96 nsec_to_clock_t(time)); 97 } 98 return len; 99 } 100 cpufreq_freq_attr_ro(time_in_state); 101 102 /* We don't care what is written to the attribute */ 103 static ssize_t store_reset(struct cpufreq_policy *policy, const char *buf, 104 size_t count) 105 { 106 struct cpufreq_stats *stats = policy->stats; 107 108 /* 109 * Defer resetting of stats to cpufreq_stats_record_transition() to 110 * avoid races. 111 */ 112 WRITE_ONCE(stats->reset_time, local_clock()); 113 /* 114 * The memory barrier below is to prevent the readers of reset_time from 115 * seeing a stale or partially updated value. 116 */ 117 smp_wmb(); 118 WRITE_ONCE(stats->reset_pending, 1); 119 120 return count; 121 } 122 cpufreq_freq_attr_wo(reset); 123 124 static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf) 125 { 126 struct cpufreq_stats *stats = policy->stats; 127 bool pending = READ_ONCE(stats->reset_pending); 128 ssize_t len = 0; 129 int i, j, count; 130 131 len += scnprintf(buf + len, PAGE_SIZE - len, " From : To\n"); 132 len += scnprintf(buf + len, PAGE_SIZE - len, " : "); 133 for (i = 0; i < stats->state_num; i++) { 134 if (len >= PAGE_SIZE) 135 break; 136 len += scnprintf(buf + len, PAGE_SIZE - len, "%9u ", 137 stats->freq_table[i]); 138 } 139 if (len >= PAGE_SIZE) 140 return PAGE_SIZE; 141 142 len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); 143 144 for (i = 0; i < stats->state_num; i++) { 145 if (len >= PAGE_SIZE) 146 break; 147 148 len += scnprintf(buf + len, PAGE_SIZE - len, "%9u: ", 149 stats->freq_table[i]); 150 151 for (j = 0; j < stats->state_num; j++) { 152 if (len >= PAGE_SIZE) 153 break; 154 155 if (pending) 156 count = 0; 157 else 158 count = stats->trans_table[i * stats->max_state + j]; 159 160 len += scnprintf(buf + len, PAGE_SIZE - len, "%9u ", count); 161 } 162 if (len >= PAGE_SIZE) 163 break; 164 len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); 165 } 166 167 if (len >= PAGE_SIZE) { 168 pr_warn_once("cpufreq transition table exceeds PAGE_SIZE. Disabling\n"); 169 return -EFBIG; 170 } 171 return len; 172 } 173 cpufreq_freq_attr_ro(trans_table); 174 175 static struct attribute *default_attrs[] = { 176 &total_trans.attr, 177 &time_in_state.attr, 178 &reset.attr, 179 &trans_table.attr, 180 NULL 181 }; 182 static const struct attribute_group stats_attr_group = { 183 .attrs = default_attrs, 184 .name = "stats" 185 }; 186 187 static int freq_table_get_index(struct cpufreq_stats *stats, unsigned int freq) 188 { 189 int index; 190 for (index = 0; index < stats->max_state; index++) 191 if (stats->freq_table[index] == freq) 192 return index; 193 return -1; 194 } 195 196 void cpufreq_stats_free_table(struct cpufreq_policy *policy) 197 { 198 struct cpufreq_stats *stats = policy->stats; 199 200 /* Already freed */ 201 if (!stats) 202 return; 203 204 pr_debug("%s: Free stats table\n", __func__); 205 206 sysfs_remove_group(&policy->kobj, &stats_attr_group); 207 kfree(stats->time_in_state); 208 kfree(stats); 209 policy->stats = NULL; 210 } 211 212 void cpufreq_stats_create_table(struct cpufreq_policy *policy) 213 { 214 unsigned int i = 0, count; 215 struct cpufreq_stats *stats; 216 unsigned int alloc_size; 217 struct cpufreq_frequency_table *pos; 218 219 count = cpufreq_table_count_valid_entries(policy); 220 if (!count) 221 return; 222 223 /* stats already initialized */ 224 if (policy->stats) 225 return; 226 227 stats = kzalloc(sizeof(*stats), GFP_KERNEL); 228 if (!stats) 229 return; 230 231 alloc_size = count * sizeof(int) + count * sizeof(u64); 232 233 alloc_size += count * count * sizeof(int); 234 235 /* Allocate memory for time_in_state/freq_table/trans_table in one go */ 236 stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL); 237 if (!stats->time_in_state) 238 goto free_stat; 239 240 stats->freq_table = (unsigned int *)(stats->time_in_state + count); 241 242 stats->trans_table = stats->freq_table + count; 243 244 stats->max_state = count; 245 246 /* Find valid-unique entries */ 247 cpufreq_for_each_valid_entry(pos, policy->freq_table) 248 if (freq_table_get_index(stats, pos->frequency) == -1) 249 stats->freq_table[i++] = pos->frequency; 250 251 stats->state_num = i; 252 stats->last_time = local_clock(); 253 stats->last_index = freq_table_get_index(stats, policy->cur); 254 255 policy->stats = stats; 256 if (!sysfs_create_group(&policy->kobj, &stats_attr_group)) 257 return; 258 259 /* We failed, release resources */ 260 policy->stats = NULL; 261 kfree(stats->time_in_state); 262 free_stat: 263 kfree(stats); 264 } 265 266 void cpufreq_stats_record_transition(struct cpufreq_policy *policy, 267 unsigned int new_freq) 268 { 269 struct cpufreq_stats *stats = policy->stats; 270 int old_index, new_index; 271 272 if (unlikely(!stats)) 273 return; 274 275 if (unlikely(READ_ONCE(stats->reset_pending))) 276 cpufreq_stats_reset_table(stats); 277 278 old_index = stats->last_index; 279 new_index = freq_table_get_index(stats, new_freq); 280 281 /* We can't do stats->time_in_state[-1]= .. */ 282 if (unlikely(old_index == -1 || new_index == -1 || old_index == new_index)) 283 return; 284 285 cpufreq_stats_update(stats, stats->last_time); 286 287 stats->last_index = new_index; 288 stats->trans_table[old_index * stats->max_state + new_index]++; 289 stats->total_trans++; 290 } 291