root/drivers/cpufreq/cpufreq_stats.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cpufreq_stats_update
  2. cpufreq_stats_clear_table
  3. show_total_trans
  4. show_time_in_state
  5. store_reset
  6. show_trans_table
  7. freq_table_get_index
  8. cpufreq_stats_free_table
  9. cpufreq_stats_create_table
  10. cpufreq_stats_record_transition

   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         spinlock_t lock;
  23         unsigned int *freq_table;
  24         unsigned int *trans_table;
  25 };
  26 
  27 static void cpufreq_stats_update(struct cpufreq_stats *stats)
  28 {
  29         unsigned long long cur_time = get_jiffies_64();
  30 
  31         stats->time_in_state[stats->last_index] += cur_time - stats->last_time;
  32         stats->last_time = cur_time;
  33 }
  34 
  35 static void cpufreq_stats_clear_table(struct cpufreq_stats *stats)
  36 {
  37         unsigned int count = stats->max_state;
  38 
  39         spin_lock(&stats->lock);
  40         memset(stats->time_in_state, 0, count * sizeof(u64));
  41         memset(stats->trans_table, 0, count * count * sizeof(int));
  42         stats->last_time = get_jiffies_64();
  43         stats->total_trans = 0;
  44         spin_unlock(&stats->lock);
  45 }
  46 
  47 static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
  48 {
  49         return sprintf(buf, "%d\n", policy->stats->total_trans);
  50 }
  51 cpufreq_freq_attr_ro(total_trans);
  52 
  53 static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
  54 {
  55         struct cpufreq_stats *stats = policy->stats;
  56         ssize_t len = 0;
  57         int i;
  58 
  59         if (policy->fast_switch_enabled)
  60                 return 0;
  61 
  62         spin_lock(&stats->lock);
  63         cpufreq_stats_update(stats);
  64         spin_unlock(&stats->lock);
  65 
  66         for (i = 0; i < stats->state_num; i++) {
  67                 len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i],
  68                         (unsigned long long)
  69                         jiffies_64_to_clock_t(stats->time_in_state[i]));
  70         }
  71         return len;
  72 }
  73 cpufreq_freq_attr_ro(time_in_state);
  74 
  75 static ssize_t store_reset(struct cpufreq_policy *policy, const char *buf,
  76                            size_t count)
  77 {
  78         /* We don't care what is written to the attribute. */
  79         cpufreq_stats_clear_table(policy->stats);
  80         return count;
  81 }
  82 cpufreq_freq_attr_wo(reset);
  83 
  84 static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
  85 {
  86         struct cpufreq_stats *stats = policy->stats;
  87         ssize_t len = 0;
  88         int i, j;
  89 
  90         if (policy->fast_switch_enabled)
  91                 return 0;
  92 
  93         len += snprintf(buf + len, PAGE_SIZE - len, "   From  :    To\n");
  94         len += snprintf(buf + len, PAGE_SIZE - len, "         : ");
  95         for (i = 0; i < stats->state_num; i++) {
  96                 if (len >= PAGE_SIZE)
  97                         break;
  98                 len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
  99                                 stats->freq_table[i]);
 100         }
 101         if (len >= PAGE_SIZE)
 102                 return PAGE_SIZE;
 103 
 104         len += snprintf(buf + len, PAGE_SIZE - len, "\n");
 105 
 106         for (i = 0; i < stats->state_num; i++) {
 107                 if (len >= PAGE_SIZE)
 108                         break;
 109 
 110                 len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ",
 111                                 stats->freq_table[i]);
 112 
 113                 for (j = 0; j < stats->state_num; j++) {
 114                         if (len >= PAGE_SIZE)
 115                                 break;
 116                         len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
 117                                         stats->trans_table[i*stats->max_state+j]);
 118                 }
 119                 if (len >= PAGE_SIZE)
 120                         break;
 121                 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
 122         }
 123 
 124         if (len >= PAGE_SIZE) {
 125                 pr_warn_once("cpufreq transition table exceeds PAGE_SIZE. Disabling\n");
 126                 return -EFBIG;
 127         }
 128         return len;
 129 }
 130 cpufreq_freq_attr_ro(trans_table);
 131 
 132 static struct attribute *default_attrs[] = {
 133         &total_trans.attr,
 134         &time_in_state.attr,
 135         &reset.attr,
 136         &trans_table.attr,
 137         NULL
 138 };
 139 static const struct attribute_group stats_attr_group = {
 140         .attrs = default_attrs,
 141         .name = "stats"
 142 };
 143 
 144 static int freq_table_get_index(struct cpufreq_stats *stats, unsigned int freq)
 145 {
 146         int index;
 147         for (index = 0; index < stats->max_state; index++)
 148                 if (stats->freq_table[index] == freq)
 149                         return index;
 150         return -1;
 151 }
 152 
 153 void cpufreq_stats_free_table(struct cpufreq_policy *policy)
 154 {
 155         struct cpufreq_stats *stats = policy->stats;
 156 
 157         /* Already freed */
 158         if (!stats)
 159                 return;
 160 
 161         pr_debug("%s: Free stats table\n", __func__);
 162 
 163         sysfs_remove_group(&policy->kobj, &stats_attr_group);
 164         kfree(stats->time_in_state);
 165         kfree(stats);
 166         policy->stats = NULL;
 167 }
 168 
 169 void cpufreq_stats_create_table(struct cpufreq_policy *policy)
 170 {
 171         unsigned int i = 0, count = 0, ret = -ENOMEM;
 172         struct cpufreq_stats *stats;
 173         unsigned int alloc_size;
 174         struct cpufreq_frequency_table *pos;
 175 
 176         count = cpufreq_table_count_valid_entries(policy);
 177         if (!count)
 178                 return;
 179 
 180         /* stats already initialized */
 181         if (policy->stats)
 182                 return;
 183 
 184         stats = kzalloc(sizeof(*stats), GFP_KERNEL);
 185         if (!stats)
 186                 return;
 187 
 188         alloc_size = count * sizeof(int) + count * sizeof(u64);
 189 
 190         alloc_size += count * count * sizeof(int);
 191 
 192         /* Allocate memory for time_in_state/freq_table/trans_table in one go */
 193         stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
 194         if (!stats->time_in_state)
 195                 goto free_stat;
 196 
 197         stats->freq_table = (unsigned int *)(stats->time_in_state + count);
 198 
 199         stats->trans_table = stats->freq_table + count;
 200 
 201         stats->max_state = count;
 202 
 203         /* Find valid-unique entries */
 204         cpufreq_for_each_valid_entry(pos, policy->freq_table)
 205                 if (freq_table_get_index(stats, pos->frequency) == -1)
 206                         stats->freq_table[i++] = pos->frequency;
 207 
 208         stats->state_num = i;
 209         stats->last_time = get_jiffies_64();
 210         stats->last_index = freq_table_get_index(stats, policy->cur);
 211         spin_lock_init(&stats->lock);
 212 
 213         policy->stats = stats;
 214         ret = sysfs_create_group(&policy->kobj, &stats_attr_group);
 215         if (!ret)
 216                 return;
 217 
 218         /* We failed, release resources */
 219         policy->stats = NULL;
 220         kfree(stats->time_in_state);
 221 free_stat:
 222         kfree(stats);
 223 }
 224 
 225 void cpufreq_stats_record_transition(struct cpufreq_policy *policy,
 226                                      unsigned int new_freq)
 227 {
 228         struct cpufreq_stats *stats = policy->stats;
 229         int old_index, new_index;
 230 
 231         if (!stats) {
 232                 pr_debug("%s: No stats found\n", __func__);
 233                 return;
 234         }
 235 
 236         old_index = stats->last_index;
 237         new_index = freq_table_get_index(stats, new_freq);
 238 
 239         /* We can't do stats->time_in_state[-1]= .. */
 240         if (old_index == -1 || new_index == -1 || old_index == new_index)
 241                 return;
 242 
 243         spin_lock(&stats->lock);
 244         cpufreq_stats_update(stats);
 245 
 246         stats->last_index = new_index;
 247         stats->trans_table[old_index * stats->max_state + new_index]++;
 248         stats->total_trans++;
 249         spin_unlock(&stats->lock);
 250 }

/* [<][>][^][v][top][bottom][index][help] */