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.c (16832B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
      4 */
      5
      6
      7#include <stdio.h>
      8#include <errno.h>
      9#include <stdlib.h>
     10#include <string.h>
     11#include <sys/types.h>
     12#include <sys/stat.h>
     13#include <fcntl.h>
     14#include <unistd.h>
     15
     16#include "cpufreq.h"
     17#include "cpupower_intern.h"
     18
     19/* CPUFREQ sysfs access **************************************************/
     20
     21/* helper function to read file from /sys into given buffer */
     22/* fname is a relative path under "cpuX/cpufreq" dir */
     23static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname,
     24					    char *buf, size_t buflen)
     25{
     26	char path[SYSFS_PATH_MAX];
     27
     28	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
     29			 cpu, fname);
     30	return cpupower_read_sysfs(path, buf, buflen);
     31}
     32
     33/* helper function to write a new value to a /sys file */
     34/* fname is a relative path under "cpuX/cpufreq" dir */
     35static unsigned int sysfs_cpufreq_write_file(unsigned int cpu,
     36					     const char *fname,
     37					     const char *value, size_t len)
     38{
     39	char path[SYSFS_PATH_MAX];
     40	int fd;
     41	ssize_t numwrite;
     42
     43	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
     44			 cpu, fname);
     45
     46	fd = open(path, O_WRONLY);
     47	if (fd == -1)
     48		return 0;
     49
     50	numwrite = write(fd, value, len);
     51	if (numwrite < 1) {
     52		close(fd);
     53		return 0;
     54	}
     55
     56	close(fd);
     57
     58	return (unsigned int) numwrite;
     59}
     60
     61/* read access to files which contain one numeric value */
     62
     63enum cpufreq_value {
     64	CPUINFO_CUR_FREQ,
     65	CPUINFO_MIN_FREQ,
     66	CPUINFO_MAX_FREQ,
     67	CPUINFO_LATENCY,
     68	SCALING_CUR_FREQ,
     69	SCALING_MIN_FREQ,
     70	SCALING_MAX_FREQ,
     71	STATS_NUM_TRANSITIONS,
     72	MAX_CPUFREQ_VALUE_READ_FILES
     73};
     74
     75static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = {
     76	[CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq",
     77	[CPUINFO_MIN_FREQ] = "cpuinfo_min_freq",
     78	[CPUINFO_MAX_FREQ] = "cpuinfo_max_freq",
     79	[CPUINFO_LATENCY]  = "cpuinfo_transition_latency",
     80	[SCALING_CUR_FREQ] = "scaling_cur_freq",
     81	[SCALING_MIN_FREQ] = "scaling_min_freq",
     82	[SCALING_MAX_FREQ] = "scaling_max_freq",
     83	[STATS_NUM_TRANSITIONS] = "stats/total_trans"
     84};
     85
     86unsigned long cpufreq_get_sysfs_value_from_table(unsigned int cpu,
     87						 const char **table,
     88						 unsigned int index,
     89						 unsigned int size)
     90{
     91	unsigned long value;
     92	unsigned int len;
     93	char linebuf[MAX_LINE_LEN];
     94	char *endp;
     95
     96	if (!table || index >= size || !table[index])
     97		return 0;
     98
     99	len = sysfs_cpufreq_read_file(cpu, table[index], linebuf,
    100				      sizeof(linebuf));
    101
    102	if (len == 0)
    103		return 0;
    104
    105	value = strtoul(linebuf, &endp, 0);
    106
    107	if (endp == linebuf || errno == ERANGE)
    108		return 0;
    109
    110	return value;
    111}
    112
    113static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu,
    114						 enum cpufreq_value which)
    115{
    116	return cpufreq_get_sysfs_value_from_table(cpu, cpufreq_value_files,
    117						  which,
    118						  MAX_CPUFREQ_VALUE_READ_FILES);
    119}
    120
    121/* read access to files which contain one string */
    122
    123enum cpufreq_string {
    124	SCALING_DRIVER,
    125	SCALING_GOVERNOR,
    126	MAX_CPUFREQ_STRING_FILES
    127};
    128
    129static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
    130	[SCALING_DRIVER] = "scaling_driver",
    131	[SCALING_GOVERNOR] = "scaling_governor",
    132};
    133
    134
    135static char *sysfs_cpufreq_get_one_string(unsigned int cpu,
    136					  enum cpufreq_string which)
    137{
    138	char linebuf[MAX_LINE_LEN];
    139	char *result;
    140	unsigned int len;
    141
    142	if (which >= MAX_CPUFREQ_STRING_FILES)
    143		return NULL;
    144
    145	len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
    146				linebuf, sizeof(linebuf));
    147	if (len == 0)
    148		return NULL;
    149
    150	result = strdup(linebuf);
    151	if (result == NULL)
    152		return NULL;
    153
    154	if (result[strlen(result) - 1] == '\n')
    155		result[strlen(result) - 1] = '\0';
    156
    157	return result;
    158}
    159
    160/* write access */
    161
    162enum cpufreq_write {
    163	WRITE_SCALING_MIN_FREQ,
    164	WRITE_SCALING_MAX_FREQ,
    165	WRITE_SCALING_GOVERNOR,
    166	WRITE_SCALING_SET_SPEED,
    167	MAX_CPUFREQ_WRITE_FILES
    168};
    169
    170static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {
    171	[WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",
    172	[WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",
    173	[WRITE_SCALING_GOVERNOR] = "scaling_governor",
    174	[WRITE_SCALING_SET_SPEED] = "scaling_setspeed",
    175};
    176
    177static int sysfs_cpufreq_write_one_value(unsigned int cpu,
    178					 enum cpufreq_write which,
    179					 const char *new_value, size_t len)
    180{
    181	if (which >= MAX_CPUFREQ_WRITE_FILES)
    182		return 0;
    183
    184	if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
    185					new_value, len) != len)
    186		return -ENODEV;
    187
    188	return 0;
    189};
    190
    191unsigned long cpufreq_get_freq_kernel(unsigned int cpu)
    192{
    193	return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
    194}
    195
    196unsigned long cpufreq_get_freq_hardware(unsigned int cpu)
    197{
    198	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
    199}
    200
    201unsigned long cpufreq_get_transition_latency(unsigned int cpu)
    202{
    203	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
    204}
    205
    206int cpufreq_get_hardware_limits(unsigned int cpu,
    207				unsigned long *min,
    208				unsigned long *max)
    209{
    210	if ((!min) || (!max))
    211		return -EINVAL;
    212
    213	*min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ);
    214	if (!*min)
    215		return -ENODEV;
    216
    217	*max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);
    218	if (!*max)
    219		return -ENODEV;
    220
    221	return 0;
    222}
    223
    224char *cpufreq_get_driver(unsigned int cpu)
    225{
    226	return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
    227}
    228
    229void cpufreq_put_driver(char *ptr)
    230{
    231	if (!ptr)
    232		return;
    233	free(ptr);
    234}
    235
    236struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu)
    237{
    238	struct cpufreq_policy *policy;
    239
    240	policy = malloc(sizeof(struct cpufreq_policy));
    241	if (!policy)
    242		return NULL;
    243
    244	policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);
    245	if (!policy->governor) {
    246		free(policy);
    247		return NULL;
    248	}
    249	policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
    250	policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ);
    251	if ((!policy->min) || (!policy->max)) {
    252		free(policy->governor);
    253		free(policy);
    254		return NULL;
    255	}
    256
    257	return policy;
    258}
    259
    260void cpufreq_put_policy(struct cpufreq_policy *policy)
    261{
    262	if ((!policy) || (!policy->governor))
    263		return;
    264
    265	free(policy->governor);
    266	policy->governor = NULL;
    267	free(policy);
    268}
    269
    270struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned
    271								int cpu)
    272{
    273	struct cpufreq_available_governors *first = NULL;
    274	struct cpufreq_available_governors *current = NULL;
    275	char linebuf[MAX_LINE_LEN];
    276	unsigned int pos, i;
    277	unsigned int len;
    278
    279	len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
    280				linebuf, sizeof(linebuf));
    281	if (len == 0)
    282		return NULL;
    283
    284	pos = 0;
    285	for (i = 0; i < len; i++) {
    286		if (linebuf[i] == ' ' || linebuf[i] == '\n') {
    287			if (i - pos < 2)
    288				continue;
    289			if (current) {
    290				current->next = malloc(sizeof(*current));
    291				if (!current->next)
    292					goto error_out;
    293				current = current->next;
    294			} else {
    295				first = malloc(sizeof(*first));
    296				if (!first)
    297					return NULL;
    298				current = first;
    299			}
    300			current->first = first;
    301			current->next = NULL;
    302
    303			current->governor = malloc(i - pos + 1);
    304			if (!current->governor)
    305				goto error_out;
    306
    307			memcpy(current->governor, linebuf + pos, i - pos);
    308			current->governor[i - pos] = '\0';
    309			pos = i + 1;
    310		}
    311	}
    312
    313	return first;
    314
    315 error_out:
    316	while (first) {
    317		current = first->next;
    318		if (first->governor)
    319			free(first->governor);
    320		free(first);
    321		first = current;
    322	}
    323	return NULL;
    324}
    325
    326void cpufreq_put_available_governors(struct cpufreq_available_governors *any)
    327{
    328	struct cpufreq_available_governors *tmp, *next;
    329
    330	if (!any)
    331		return;
    332
    333	tmp = any->first;
    334	while (tmp) {
    335		next = tmp->next;
    336		if (tmp->governor)
    337			free(tmp->governor);
    338		free(tmp);
    339		tmp = next;
    340	}
    341}
    342
    343
    344struct cpufreq_available_frequencies
    345*cpufreq_get_available_frequencies(unsigned int cpu)
    346{
    347	struct cpufreq_available_frequencies *first = NULL;
    348	struct cpufreq_available_frequencies *current = NULL;
    349	char one_value[SYSFS_PATH_MAX];
    350	char linebuf[MAX_LINE_LEN];
    351	unsigned int pos, i;
    352	unsigned int len;
    353
    354	len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies",
    355				      linebuf, sizeof(linebuf));
    356	if (len == 0)
    357		return NULL;
    358
    359	pos = 0;
    360	for (i = 0; i < len; i++) {
    361		if (linebuf[i] == ' ' || linebuf[i] == '\n') {
    362			if (i - pos < 2)
    363				continue;
    364			if (i - pos >= SYSFS_PATH_MAX)
    365				goto error_out;
    366			if (current) {
    367				current->next = malloc(sizeof(*current));
    368				if (!current->next)
    369					goto error_out;
    370				current = current->next;
    371			} else {
    372				first = malloc(sizeof(*first));
    373				if (!first)
    374					return NULL;
    375				current = first;
    376			}
    377			current->first = first;
    378			current->next = NULL;
    379
    380			memcpy(one_value, linebuf + pos, i - pos);
    381			one_value[i - pos] = '\0';
    382			if (sscanf(one_value, "%lu", &current->frequency) != 1)
    383				goto error_out;
    384
    385			pos = i + 1;
    386		}
    387	}
    388
    389	return first;
    390
    391 error_out:
    392	while (first) {
    393		current = first->next;
    394		free(first);
    395		first = current;
    396	}
    397	return NULL;
    398}
    399
    400struct cpufreq_available_frequencies
    401*cpufreq_get_boost_frequencies(unsigned int cpu)
    402{
    403	struct cpufreq_available_frequencies *first = NULL;
    404	struct cpufreq_available_frequencies *current = NULL;
    405	char one_value[SYSFS_PATH_MAX];
    406	char linebuf[MAX_LINE_LEN];
    407	unsigned int pos, i;
    408	unsigned int len;
    409
    410	len = sysfs_cpufreq_read_file(cpu, "scaling_boost_frequencies",
    411				      linebuf, sizeof(linebuf));
    412	if (len == 0)
    413		return NULL;
    414
    415	pos = 0;
    416	for (i = 0; i < len; i++) {
    417		if (linebuf[i] == ' ' || linebuf[i] == '\n') {
    418			if (i - pos < 2)
    419				continue;
    420			if (i - pos >= SYSFS_PATH_MAX)
    421				goto error_out;
    422			if (current) {
    423				current->next = malloc(sizeof(*current));
    424				if (!current->next)
    425					goto error_out;
    426				current = current->next;
    427			} else {
    428				first = malloc(sizeof(*first));
    429				if (!first)
    430					return NULL;
    431				current = first;
    432			}
    433			current->first = first;
    434			current->next = NULL;
    435
    436			memcpy(one_value, linebuf + pos, i - pos);
    437			one_value[i - pos] = '\0';
    438			if (sscanf(one_value, "%lu", &current->frequency) != 1)
    439				goto error_out;
    440
    441			pos = i + 1;
    442		}
    443	}
    444
    445	return first;
    446
    447 error_out:
    448	while (first) {
    449		current = first->next;
    450		free(first);
    451		first = current;
    452	}
    453	return NULL;
    454}
    455
    456void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies *any)
    457{
    458	struct cpufreq_available_frequencies *tmp, *next;
    459
    460	if (!any)
    461		return;
    462
    463	tmp = any->first;
    464	while (tmp) {
    465		next = tmp->next;
    466		free(tmp);
    467		tmp = next;
    468	}
    469}
    470
    471void cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies *any)
    472{
    473	cpufreq_put_available_frequencies(any);
    474}
    475
    476static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,
    477							const char *file)
    478{
    479	struct cpufreq_affected_cpus *first = NULL;
    480	struct cpufreq_affected_cpus *current = NULL;
    481	char one_value[SYSFS_PATH_MAX];
    482	char linebuf[MAX_LINE_LEN];
    483	unsigned int pos, i;
    484	unsigned int len;
    485
    486	len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));
    487	if (len == 0)
    488		return NULL;
    489
    490	pos = 0;
    491	for (i = 0; i < len; i++) {
    492		if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {
    493			if (i - pos  < 1)
    494				continue;
    495			if (i - pos >= SYSFS_PATH_MAX)
    496				goto error_out;
    497			if (current) {
    498				current->next = malloc(sizeof(*current));
    499				if (!current->next)
    500					goto error_out;
    501				current = current->next;
    502			} else {
    503				first = malloc(sizeof(*first));
    504				if (!first)
    505					return NULL;
    506				current = first;
    507			}
    508			current->first = first;
    509			current->next = NULL;
    510
    511			memcpy(one_value, linebuf + pos, i - pos);
    512			one_value[i - pos] = '\0';
    513
    514			if (sscanf(one_value, "%u", &current->cpu) != 1)
    515				goto error_out;
    516
    517			pos = i + 1;
    518		}
    519	}
    520
    521	return first;
    522
    523 error_out:
    524	while (first) {
    525		current = first->next;
    526		free(first);
    527		first = current;
    528	}
    529	return NULL;
    530}
    531
    532struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu)
    533{
    534	return sysfs_get_cpu_list(cpu, "affected_cpus");
    535}
    536
    537void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any)
    538{
    539	struct cpufreq_affected_cpus *tmp, *next;
    540
    541	if (!any)
    542		return;
    543
    544	tmp = any->first;
    545	while (tmp) {
    546		next = tmp->next;
    547		free(tmp);
    548		tmp = next;
    549	}
    550}
    551
    552
    553struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu)
    554{
    555	return sysfs_get_cpu_list(cpu, "related_cpus");
    556}
    557
    558void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any)
    559{
    560	cpufreq_put_affected_cpus(any);
    561}
    562
    563static int verify_gov(char *new_gov, char *passed_gov)
    564{
    565	unsigned int i, j = 0;
    566
    567	if (!passed_gov || (strlen(passed_gov) > 19))
    568		return -EINVAL;
    569
    570	strncpy(new_gov, passed_gov, 20);
    571	for (i = 0; i < 20; i++) {
    572		if (j) {
    573			new_gov[i] = '\0';
    574			continue;
    575		}
    576		if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z'))
    577			continue;
    578
    579		if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z'))
    580			continue;
    581
    582		if (new_gov[i] == '-')
    583			continue;
    584
    585		if (new_gov[i] == '_')
    586			continue;
    587
    588		if (new_gov[i] == '\0') {
    589			j = 1;
    590			continue;
    591		}
    592		return -EINVAL;
    593	}
    594	new_gov[19] = '\0';
    595	return 0;
    596}
    597
    598int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy)
    599{
    600	char min[SYSFS_PATH_MAX];
    601	char max[SYSFS_PATH_MAX];
    602	char gov[SYSFS_PATH_MAX];
    603	int ret;
    604	unsigned long old_min;
    605	int write_max_first;
    606
    607	if (!policy || !(policy->governor))
    608		return -EINVAL;
    609
    610	if (policy->max < policy->min)
    611		return -EINVAL;
    612
    613	if (verify_gov(gov, policy->governor))
    614		return -EINVAL;
    615
    616	snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);
    617	snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);
    618
    619	old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
    620	write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);
    621
    622	if (write_max_first) {
    623		ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
    624						    max, strlen(max));
    625		if (ret)
    626			return ret;
    627	}
    628
    629	ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
    630					    strlen(min));
    631	if (ret)
    632		return ret;
    633
    634	if (!write_max_first) {
    635		ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
    636						    max, strlen(max));
    637		if (ret)
    638			return ret;
    639	}
    640
    641	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
    642					     gov, strlen(gov));
    643}
    644
    645
    646int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq)
    647{
    648	char value[SYSFS_PATH_MAX];
    649
    650	snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);
    651
    652	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,
    653					     value, strlen(value));
    654}
    655
    656
    657int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq)
    658{
    659	char value[SYSFS_PATH_MAX];
    660
    661	snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);
    662
    663	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
    664					     value, strlen(value));
    665}
    666
    667int cpufreq_modify_policy_governor(unsigned int cpu, char *governor)
    668{
    669	char new_gov[SYSFS_PATH_MAX];
    670
    671	if ((!governor) || (strlen(governor) > 19))
    672		return -EINVAL;
    673
    674	if (verify_gov(new_gov, governor))
    675		return -EINVAL;
    676
    677	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
    678					     new_gov, strlen(new_gov));
    679}
    680
    681int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency)
    682{
    683	struct cpufreq_policy *pol = cpufreq_get_policy(cpu);
    684	char userspace_gov[] = "userspace";
    685	char freq[SYSFS_PATH_MAX];
    686	int ret;
    687
    688	if (!pol)
    689		return -ENODEV;
    690
    691	if (strncmp(pol->governor, userspace_gov, 9) != 0) {
    692		ret = cpufreq_modify_policy_governor(cpu, userspace_gov);
    693		if (ret) {
    694			cpufreq_put_policy(pol);
    695			return ret;
    696		}
    697	}
    698
    699	cpufreq_put_policy(pol);
    700
    701	snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
    702
    703	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
    704					     freq, strlen(freq));
    705}
    706
    707struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu,
    708					unsigned long long *total_time)
    709{
    710	struct cpufreq_stats *first = NULL;
    711	struct cpufreq_stats *current = NULL;
    712	char one_value[SYSFS_PATH_MAX];
    713	char linebuf[MAX_LINE_LEN];
    714	unsigned int pos, i;
    715	unsigned int len;
    716
    717	len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
    718				linebuf, sizeof(linebuf));
    719	if (len == 0)
    720		return NULL;
    721
    722	*total_time = 0;
    723	pos = 0;
    724	for (i = 0; i < len; i++) {
    725		if (i == strlen(linebuf) || linebuf[i] == '\n')	{
    726			if (i - pos < 2)
    727				continue;
    728			if ((i - pos) >= SYSFS_PATH_MAX)
    729				goto error_out;
    730			if (current) {
    731				current->next = malloc(sizeof(*current));
    732				if (!current->next)
    733					goto error_out;
    734				current = current->next;
    735			} else {
    736				first = malloc(sizeof(*first));
    737				if (!first)
    738					return NULL;
    739				current = first;
    740			}
    741			current->first = first;
    742			current->next = NULL;
    743
    744			memcpy(one_value, linebuf + pos, i - pos);
    745			one_value[i - pos] = '\0';
    746			if (sscanf(one_value, "%lu %llu",
    747					&current->frequency,
    748					&current->time_in_state) != 2)
    749				goto error_out;
    750
    751			*total_time = *total_time + current->time_in_state;
    752			pos = i + 1;
    753		}
    754	}
    755
    756	return first;
    757
    758 error_out:
    759	while (first) {
    760		current = first->next;
    761		free(first);
    762		first = current;
    763	}
    764	return NULL;
    765}
    766
    767void cpufreq_put_stats(struct cpufreq_stats *any)
    768{
    769	struct cpufreq_stats *tmp, *next;
    770
    771	if (!any)
    772		return;
    773
    774	tmp = any->first;
    775	while (tmp) {
    776		next = tmp->next;
    777		free(tmp);
    778		tmp = next;
    779	}
    780}
    781
    782unsigned long cpufreq_get_transitions(unsigned int cpu)
    783{
    784	return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
    785}