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

cpuidle.c (8802B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
      4 *  (C) 2011       Thomas Renninger <trenn@novell.com> Novell Inc.
      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 "cpuidle.h"
     17#include "cpupower_intern.h"
     18
     19/*
     20 * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir
     21 * exists.
     22 * For example the functionality to disable c-states was introduced in later
     23 * kernel versions, this function can be used to explicitly check for this
     24 * feature.
     25 *
     26 * returns 1 if the file exists, 0 otherwise.
     27 */
     28static
     29unsigned int cpuidle_state_file_exists(unsigned int cpu,
     30				       unsigned int idlestate,
     31				       const char *fname)
     32{
     33	char path[SYSFS_PATH_MAX];
     34	struct stat statbuf;
     35
     36
     37	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
     38		 cpu, idlestate, fname);
     39	if (stat(path, &statbuf) != 0)
     40		return 0;
     41	return 1;
     42}
     43
     44/*
     45 * helper function to read file from /sys into given buffer
     46 * fname is a relative path under "cpuX/cpuidle/stateX/" dir
     47 * cstates starting with 0, C0 is not counted as cstate.
     48 * This means if you want C1 info, pass 0 as idlestate param
     49 */
     50static
     51unsigned int cpuidle_state_read_file(unsigned int cpu,
     52					    unsigned int idlestate,
     53					    const char *fname, char *buf,
     54					    size_t buflen)
     55{
     56	char path[SYSFS_PATH_MAX];
     57	int fd;
     58	ssize_t numread;
     59
     60	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
     61		 cpu, idlestate, fname);
     62
     63	fd = open(path, O_RDONLY);
     64	if (fd == -1)
     65		return 0;
     66
     67	numread = read(fd, buf, buflen - 1);
     68	if (numread < 1) {
     69		close(fd);
     70		return 0;
     71	}
     72
     73	buf[numread] = '\0';
     74	close(fd);
     75
     76	return (unsigned int) numread;
     77}
     78
     79/*
     80 * helper function to write a new value to a /sys file
     81 * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir
     82 *
     83 * Returns the number of bytes written or 0 on error
     84 */
     85static
     86unsigned int cpuidle_state_write_file(unsigned int cpu,
     87				      unsigned int idlestate,
     88				      const char *fname,
     89				      const char *value, size_t len)
     90{
     91	char path[SYSFS_PATH_MAX];
     92	int fd;
     93	ssize_t numwrite;
     94
     95	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
     96		 cpu, idlestate, fname);
     97
     98	fd = open(path, O_WRONLY);
     99	if (fd == -1)
    100		return 0;
    101
    102	numwrite = write(fd, value, len);
    103	if (numwrite < 1) {
    104		close(fd);
    105		return 0;
    106	}
    107
    108	close(fd);
    109
    110	return (unsigned int) numwrite;
    111}
    112
    113/* read access to files which contain one numeric value */
    114
    115enum idlestate_value {
    116	IDLESTATE_USAGE,
    117	IDLESTATE_POWER,
    118	IDLESTATE_LATENCY,
    119	IDLESTATE_TIME,
    120	IDLESTATE_DISABLE,
    121	MAX_IDLESTATE_VALUE_FILES
    122};
    123
    124static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {
    125	[IDLESTATE_USAGE] = "usage",
    126	[IDLESTATE_POWER] = "power",
    127	[IDLESTATE_LATENCY] = "latency",
    128	[IDLESTATE_TIME]  = "time",
    129	[IDLESTATE_DISABLE]  = "disable",
    130};
    131
    132static
    133unsigned long long cpuidle_state_get_one_value(unsigned int cpu,
    134					       unsigned int idlestate,
    135					       enum idlestate_value which)
    136{
    137	unsigned long long value;
    138	unsigned int len;
    139	char linebuf[MAX_LINE_LEN];
    140	char *endp;
    141
    142	if (which >= MAX_IDLESTATE_VALUE_FILES)
    143		return 0;
    144
    145	len = cpuidle_state_read_file(cpu, idlestate,
    146				      idlestate_value_files[which],
    147				      linebuf, sizeof(linebuf));
    148	if (len == 0)
    149		return 0;
    150
    151	value = strtoull(linebuf, &endp, 0);
    152
    153	if (endp == linebuf || errno == ERANGE)
    154		return 0;
    155
    156	return value;
    157}
    158
    159/* read access to files which contain one string */
    160
    161enum idlestate_string {
    162	IDLESTATE_DESC,
    163	IDLESTATE_NAME,
    164	MAX_IDLESTATE_STRING_FILES
    165};
    166
    167static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
    168	[IDLESTATE_DESC] = "desc",
    169	[IDLESTATE_NAME] = "name",
    170};
    171
    172
    173static char *cpuidle_state_get_one_string(unsigned int cpu,
    174					unsigned int idlestate,
    175					enum idlestate_string which)
    176{
    177	char linebuf[MAX_LINE_LEN];
    178	char *result;
    179	unsigned int len;
    180
    181	if (which >= MAX_IDLESTATE_STRING_FILES)
    182		return NULL;
    183
    184	len = cpuidle_state_read_file(cpu, idlestate,
    185				      idlestate_string_files[which],
    186				      linebuf, sizeof(linebuf));
    187	if (len == 0)
    188		return NULL;
    189
    190	result = strdup(linebuf);
    191	if (result == NULL)
    192		return NULL;
    193
    194	if (result[strlen(result) - 1] == '\n')
    195		result[strlen(result) - 1] = '\0';
    196
    197	return result;
    198}
    199
    200/*
    201 * Returns:
    202 *    1  if disabled
    203 *    0  if enabled
    204 *    -1 if idlestate is not available
    205 *    -2 if disabling is not supported by the kernel
    206 */
    207int cpuidle_is_state_disabled(unsigned int cpu,
    208				unsigned int idlestate)
    209{
    210	if (cpuidle_state_count(cpu) <= idlestate)
    211		return -1;
    212
    213	if (!cpuidle_state_file_exists(cpu, idlestate,
    214				 idlestate_value_files[IDLESTATE_DISABLE]))
    215		return -2;
    216	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);
    217}
    218
    219/*
    220 * Pass 1 as last argument to disable or 0 to enable the state
    221 * Returns:
    222 *    0  on success
    223 *    negative values on error, for example:
    224 *      -1 if idlestate is not available
    225 *      -2 if disabling is not supported by the kernel
    226 *      -3 No write access to disable/enable C-states
    227 */
    228int cpuidle_state_disable(unsigned int cpu,
    229			    unsigned int idlestate,
    230			    unsigned int disable)
    231{
    232	char value[SYSFS_PATH_MAX];
    233	int bytes_written;
    234
    235	if (cpuidle_state_count(cpu) <= idlestate)
    236		return -1;
    237
    238	if (!cpuidle_state_file_exists(cpu, idlestate,
    239				 idlestate_value_files[IDLESTATE_DISABLE]))
    240		return -2;
    241
    242	snprintf(value, SYSFS_PATH_MAX, "%u", disable);
    243
    244	bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable",
    245						   value, sizeof(disable));
    246	if (bytes_written)
    247		return 0;
    248	return -3;
    249}
    250
    251unsigned long cpuidle_state_latency(unsigned int cpu,
    252					  unsigned int idlestate)
    253{
    254	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
    255}
    256
    257unsigned long cpuidle_state_usage(unsigned int cpu,
    258					unsigned int idlestate)
    259{
    260	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
    261}
    262
    263unsigned long long cpuidle_state_time(unsigned int cpu,
    264					unsigned int idlestate)
    265{
    266	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME);
    267}
    268
    269char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate)
    270{
    271	return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME);
    272}
    273
    274char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate)
    275{
    276	return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC);
    277}
    278
    279/*
    280 * Returns number of supported C-states of CPU core cpu
    281 * Negativ in error case
    282 * Zero if cpuidle does not export any C-states
    283 */
    284unsigned int cpuidle_state_count(unsigned int cpu)
    285{
    286	char file[SYSFS_PATH_MAX];
    287	struct stat statbuf;
    288	int idlestates = 1;
    289
    290
    291	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
    292	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
    293		return 0;
    294
    295	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
    296	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
    297		return 0;
    298
    299	while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
    300		snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
    301			 "cpu%u/cpuidle/state%d", cpu, idlestates);
    302		idlestates++;
    303	}
    304	idlestates--;
    305	return idlestates;
    306}
    307
    308/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
    309
    310/*
    311 * helper function to read file from /sys into given buffer
    312 * fname is a relative path under "cpu/cpuidle/" dir
    313 */
    314static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
    315					    size_t buflen)
    316{
    317	char path[SYSFS_PATH_MAX];
    318
    319	snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
    320
    321	return cpupower_read_sysfs(path, buf, buflen);
    322}
    323
    324
    325
    326/* read access to files which contain one string */
    327
    328enum cpuidle_string {
    329	CPUIDLE_GOVERNOR,
    330	CPUIDLE_GOVERNOR_RO,
    331	CPUIDLE_DRIVER,
    332	MAX_CPUIDLE_STRING_FILES
    333};
    334
    335static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
    336	[CPUIDLE_GOVERNOR]	= "current_governor",
    337	[CPUIDLE_GOVERNOR_RO]	= "current_governor_ro",
    338	[CPUIDLE_DRIVER]	= "current_driver",
    339};
    340
    341
    342static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
    343{
    344	char linebuf[MAX_LINE_LEN];
    345	char *result;
    346	unsigned int len;
    347
    348	if (which >= MAX_CPUIDLE_STRING_FILES)
    349		return NULL;
    350
    351	len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
    352				linebuf, sizeof(linebuf));
    353	if (len == 0)
    354		return NULL;
    355
    356	result = strdup(linebuf);
    357	if (result == NULL)
    358		return NULL;
    359
    360	if (result[strlen(result) - 1] == '\n')
    361		result[strlen(result) - 1] = '\0';
    362
    363	return result;
    364}
    365
    366char *cpuidle_get_governor(void)
    367{
    368	char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
    369	if (!tmp)
    370		return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
    371	else
    372		return tmp;
    373}
    374
    375char *cpuidle_get_driver(void)
    376{
    377	return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
    378}
    379/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */