LCOV - code coverage report
Current view: top level - drivers/cpufreq - cpufreq_stats.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_real_modules_combined.info Lines: 40 95 42.1 %
Date: 2020-09-30 20:25:40 Functions: 3 9 33.3 %
Branches: 20 62 32.3 %

           Branch data     Line data    Source code
       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                 :      18102 : static void cpufreq_stats_update(struct cpufreq_stats *stats)
      28                 :            : {
      29                 :      18102 :         unsigned long long cur_time = get_jiffies_64();
      30                 :            : 
      31                 :      18102 :         stats->time_in_state[stats->last_index] += cur_time - stats->last_time;
      32                 :      18102 :         stats->last_time = cur_time;
      33                 :      18102 : }
      34                 :            : 
      35                 :          0 : static void cpufreq_stats_clear_table(struct cpufreq_stats *stats)
      36                 :            : {
      37                 :          0 :         unsigned int count = stats->max_state;
      38                 :            : 
      39                 :            :         spin_lock(&stats->lock);
      40                 :          0 :         memset(stats->time_in_state, 0, count * sizeof(u64));
      41                 :          0 :         memset(stats->trans_table, 0, count * count * sizeof(int));
      42                 :          0 :         stats->last_time = get_jiffies_64();
      43                 :          0 :         stats->total_trans = 0;
      44                 :            :         spin_unlock(&stats->lock);
      45                 :          0 : }
      46                 :            : 
      47                 :          0 : static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
      48                 :            : {
      49                 :          0 :         return sprintf(buf, "%d\n", policy->stats->total_trans);
      50                 :            : }
      51                 :            : cpufreq_freq_attr_ro(total_trans);
      52                 :            : 
      53                 :          0 : static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
      54                 :            : {
      55                 :          0 :         struct cpufreq_stats *stats = policy->stats;
      56                 :            :         ssize_t len = 0;
      57                 :            :         int i;
      58                 :            : 
      59         [ #  # ]:          0 :         if (policy->fast_switch_enabled)
      60                 :            :                 return 0;
      61                 :            : 
      62                 :            :         spin_lock(&stats->lock);
      63                 :          0 :         cpufreq_stats_update(stats);
      64                 :            :         spin_unlock(&stats->lock);
      65                 :            : 
      66         [ #  # ]:          0 :         for (i = 0; i < stats->state_num; i++) {
      67                 :          0 :                 len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i],
      68                 :            :                         (unsigned long long)
      69                 :          0 :                         jiffies_64_to_clock_t(stats->time_in_state[i]));
      70                 :            :         }
      71                 :          0 :         return len;
      72                 :            : }
      73                 :            : cpufreq_freq_attr_ro(time_in_state);
      74                 :            : 
      75                 :          0 : 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                 :          0 :         cpufreq_stats_clear_table(policy->stats);
      80                 :          0 :         return count;
      81                 :            : }
      82                 :            : cpufreq_freq_attr_wo(reset);
      83                 :            : 
      84                 :          0 : static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
      85                 :            : {
      86                 :          0 :         struct cpufreq_stats *stats = policy->stats;
      87                 :            :         ssize_t len = 0;
      88                 :            :         int i, j;
      89                 :            : 
      90         [ #  # ]:          0 :         if (policy->fast_switch_enabled)
      91                 :            :                 return 0;
      92                 :            : 
      93                 :          0 :         len += snprintf(buf + len, PAGE_SIZE - len, "   From  :    To\n");
      94                 :          0 :         len += snprintf(buf + len, PAGE_SIZE - len, "         : ");
      95         [ #  # ]:          0 :         for (i = 0; i < stats->state_num; i++) {
      96         [ #  # ]:          0 :                 if (len >= PAGE_SIZE)
      97                 :            :                         break;
      98                 :          0 :                 len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
      99                 :          0 :                                 stats->freq_table[i]);
     100                 :            :         }
     101         [ #  # ]:          0 :         if (len >= PAGE_SIZE)
     102                 :            :                 return PAGE_SIZE;
     103                 :            : 
     104                 :          0 :         len += snprintf(buf + len, PAGE_SIZE - len, "\n");
     105                 :            : 
     106         [ #  # ]:          0 :         for (i = 0; i < stats->state_num; i++) {
     107         [ #  # ]:          0 :                 if (len >= PAGE_SIZE)
     108                 :            :                         break;
     109                 :            : 
     110                 :          0 :                 len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ",
     111                 :          0 :                                 stats->freq_table[i]);
     112                 :            : 
     113         [ #  # ]:          0 :                 for (j = 0; j < stats->state_num; j++) {
     114         [ #  # ]:          0 :                         if (len >= PAGE_SIZE)
     115                 :            :                                 break;
     116                 :          0 :                         len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
     117                 :          0 :                                         stats->trans_table[i*stats->max_state+j]);
     118                 :            :                 }
     119         [ #  # ]:          0 :                 if (len >= PAGE_SIZE)
     120                 :            :                         break;
     121                 :          0 :                 len += snprintf(buf + len, PAGE_SIZE - len, "\n");
     122                 :            :         }
     123                 :            : 
     124         [ #  # ]:          0 :         if (len >= PAGE_SIZE) {
     125         [ #  # ]:          0 :                 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   [ +  -  +  +  :      37196 :         for (index = 0; index < stats->max_state; index++)
                   +  - ]
     148   [ +  +  -  +  :      55505 :                 if (stats->freq_table[index] == freq)
                   +  - ]
     149                 :      18309 :                         return index;
     150                 :            :         return -1;
     151                 :            : }
     152                 :            : 
     153                 :          0 : void cpufreq_stats_free_table(struct cpufreq_policy *policy)
     154                 :            : {
     155                 :          0 :         struct cpufreq_stats *stats = policy->stats;
     156                 :            : 
     157                 :            :         /* Already freed */
     158         [ #  # ]:          0 :         if (!stats)
     159                 :          0 :                 return;
     160                 :            : 
     161                 :            :         pr_debug("%s: Free stats table\n", __func__);
     162                 :            : 
     163                 :          0 :         sysfs_remove_group(&policy->kobj, &stats_attr_group);
     164                 :          0 :         kfree(stats->time_in_state);
     165                 :          0 :         kfree(stats);
     166                 :          0 :         policy->stats = NULL;
     167                 :            : }
     168                 :            : 
     169                 :        207 : 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                 :        207 :         count = cpufreq_table_count_valid_entries(policy);
     177         [ +  - ]:        207 :         if (!count)
     178                 :            :                 return;
     179                 :            : 
     180                 :            :         /* stats already initialized */
     181         [ +  - ]:        207 :         if (policy->stats)
     182                 :            :                 return;
     183                 :            : 
     184                 :        207 :         stats = kzalloc(sizeof(*stats), GFP_KERNEL);
     185         [ +  - ]:        207 :         if (!stats)
     186                 :            :                 return;
     187                 :            : 
     188                 :        207 :         alloc_size = count * sizeof(int) + count * sizeof(u64);
     189                 :            : 
     190                 :        207 :         alloc_size += count * count * sizeof(int);
     191                 :            : 
     192                 :            :         /* Allocate memory for time_in_state/freq_table/trans_table in one go */
     193                 :        207 :         stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
     194         [ +  - ]:        207 :         if (!stats->time_in_state)
     195                 :            :                 goto free_stat;
     196                 :            : 
     197                 :        207 :         stats->freq_table = (unsigned int *)(stats->time_in_state + count);
     198                 :            : 
     199                 :        207 :         stats->trans_table = stats->freq_table + count;
     200                 :            : 
     201                 :        207 :         stats->max_state = count;
     202                 :            : 
     203                 :            :         /* Find valid-unique entries */
     204   [ +  -  +  + ]:       1035 :         cpufreq_for_each_valid_entry(pos, policy->freq_table)
     205         [ +  - ]:        828 :                 if (freq_table_get_index(stats, pos->frequency) == -1)
     206                 :        828 :                         stats->freq_table[i++] = pos->frequency;
     207                 :            : 
     208                 :        207 :         stats->state_num = i;
     209                 :        207 :         stats->last_time = get_jiffies_64();
     210                 :        414 :         stats->last_index = freq_table_get_index(stats, policy->cur);
     211                 :        207 :         spin_lock_init(&stats->lock);
     212                 :            : 
     213                 :        207 :         policy->stats = stats;
     214                 :        207 :         ret = sysfs_create_group(&policy->kobj, &stats_attr_group);
     215         [ -  + ]:        207 :         if (!ret)
     216                 :            :                 return;
     217                 :            : 
     218                 :            :         /* We failed, release resources */
     219                 :          0 :         policy->stats = NULL;
     220                 :          0 :         kfree(stats->time_in_state);
     221                 :            : free_stat:
     222                 :          0 :         kfree(stats);
     223                 :            : }
     224                 :            : 
     225                 :      18102 : void cpufreq_stats_record_transition(struct cpufreq_policy *policy,
     226                 :            :                                      unsigned int new_freq)
     227                 :            : {
     228                 :      18102 :         struct cpufreq_stats *stats = policy->stats;
     229                 :            :         int old_index, new_index;
     230                 :            : 
     231         [ +  - ]:      18102 :         if (!stats) {
     232                 :            :                 pr_debug("%s: No stats found\n", __func__);
     233                 :            :                 return;
     234                 :            :         }
     235                 :            : 
     236                 :      18102 :         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   [ +  -  +  - ]:      18102 :         if (old_index == -1 || new_index == -1 || old_index == new_index)
     241                 :            :                 return;
     242                 :            : 
     243                 :            :         spin_lock(&stats->lock);
     244                 :      18102 :         cpufreq_stats_update(stats);
     245                 :            : 
     246                 :      18102 :         stats->last_index = new_index;
     247                 :      18102 :         stats->trans_table[old_index * stats->max_state + new_index]++;
     248                 :      18102 :         stats->total_trans++;
     249                 :            :         spin_unlock(&stats->lock);
     250                 :          0 : }

Generated by: LCOV version 1.14