cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cpufreq_stats.c (7260B)


      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
     15struct 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
     30static 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
     39static 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
     58static 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}
     67cpufreq_freq_attr_ro(total_trans);
     68
     69static 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}
    100cpufreq_freq_attr_ro(time_in_state);
    101
    102/* We don't care what is written to the attribute */
    103static 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}
    122cpufreq_freq_attr_wo(reset);
    123
    124static 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}
    173cpufreq_freq_attr_ro(trans_table);
    174
    175static struct attribute *default_attrs[] = {
    176	&total_trans.attr,
    177	&time_in_state.attr,
    178	&reset.attr,
    179	&trans_table.attr,
    180	NULL
    181};
    182static const struct attribute_group stats_attr_group = {
    183	.attrs = default_attrs,
    184	.name = "stats"
    185};
    186
    187static 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
    196void 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
    212void 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);
    262free_stat:
    263	kfree(stats);
    264}
    265
    266void 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}