388 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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/" dir21 * exists.22 * For example the functionality to disable c-states was introduced in later23 * kernel versions, this function can be used to explicitly check for this24 * feature.25 *26 * returns 1 if the file exists, 0 otherwise.27 */28static29unsigned 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 buffer46 * fname is a relative path under "cpuX/cpuidle/stateX/" dir47 * cstates starting with 0, C0 is not counted as cstate.48 * This means if you want C1 info, pass 0 as idlestate param49 */50static51unsigned 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 file81 * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir82 *83 * Returns the number of bytes written or 0 on error84 */85static86unsigned 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_RESIDENCY,120 IDLESTATE_TIME,121 IDLESTATE_DISABLE,122 MAX_IDLESTATE_VALUE_FILES123};124 125static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {126 [IDLESTATE_USAGE] = "usage",127 [IDLESTATE_POWER] = "power",128 [IDLESTATE_LATENCY] = "latency",129 [IDLESTATE_RESIDENCY] = "residency",130 [IDLESTATE_TIME] = "time",131 [IDLESTATE_DISABLE] = "disable",132};133 134static135unsigned long long cpuidle_state_get_one_value(unsigned int cpu,136 unsigned int idlestate,137 enum idlestate_value which)138{139 unsigned long long value;140 unsigned int len;141 char linebuf[MAX_LINE_LEN];142 char *endp;143 144 if (which >= MAX_IDLESTATE_VALUE_FILES)145 return 0;146 147 len = cpuidle_state_read_file(cpu, idlestate,148 idlestate_value_files[which],149 linebuf, sizeof(linebuf));150 if (len == 0)151 return 0;152 153 value = strtoull(linebuf, &endp, 0);154 155 if (endp == linebuf || errno == ERANGE)156 return 0;157 158 return value;159}160 161/* read access to files which contain one string */162 163enum idlestate_string {164 IDLESTATE_DESC,165 IDLESTATE_NAME,166 MAX_IDLESTATE_STRING_FILES167};168 169static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {170 [IDLESTATE_DESC] = "desc",171 [IDLESTATE_NAME] = "name",172};173 174 175static char *cpuidle_state_get_one_string(unsigned int cpu,176 unsigned int idlestate,177 enum idlestate_string which)178{179 char linebuf[MAX_LINE_LEN];180 char *result;181 unsigned int len;182 183 if (which >= MAX_IDLESTATE_STRING_FILES)184 return NULL;185 186 len = cpuidle_state_read_file(cpu, idlestate,187 idlestate_string_files[which],188 linebuf, sizeof(linebuf));189 if (len == 0)190 return NULL;191 192 result = strdup(linebuf);193 if (result == NULL)194 return NULL;195 196 if (result[strlen(result) - 1] == '\n')197 result[strlen(result) - 1] = '\0';198 199 return result;200}201 202/*203 * Returns:204 * 1 if disabled205 * 0 if enabled206 * -1 if idlestate is not available207 * -2 if disabling is not supported by the kernel208 */209int cpuidle_is_state_disabled(unsigned int cpu,210 unsigned int idlestate)211{212 if (cpuidle_state_count(cpu) <= idlestate)213 return -1;214 215 if (!cpuidle_state_file_exists(cpu, idlestate,216 idlestate_value_files[IDLESTATE_DISABLE]))217 return -2;218 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);219}220 221/*222 * Pass 1 as last argument to disable or 0 to enable the state223 * Returns:224 * 0 on success225 * negative values on error, for example:226 * -1 if idlestate is not available227 * -2 if disabling is not supported by the kernel228 * -3 No write access to disable/enable C-states229 */230int cpuidle_state_disable(unsigned int cpu,231 unsigned int idlestate,232 unsigned int disable)233{234 char value[SYSFS_PATH_MAX];235 int bytes_written;236 237 if (cpuidle_state_count(cpu) <= idlestate)238 return -1;239 240 if (!cpuidle_state_file_exists(cpu, idlestate,241 idlestate_value_files[IDLESTATE_DISABLE]))242 return -2;243 244 snprintf(value, SYSFS_PATH_MAX, "%u", disable);245 246 bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable",247 value, sizeof(disable));248 if (bytes_written)249 return 0;250 return -3;251}252 253unsigned long cpuidle_state_latency(unsigned int cpu,254 unsigned int idlestate)255{256 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);257}258 259unsigned long cpuidle_state_residency(unsigned int cpu,260 unsigned int idlestate)261{262 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_RESIDENCY);263}264 265unsigned long cpuidle_state_usage(unsigned int cpu,266 unsigned int idlestate)267{268 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE);269}270 271unsigned long long cpuidle_state_time(unsigned int cpu,272 unsigned int idlestate)273{274 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME);275}276 277char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate)278{279 return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME);280}281 282char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate)283{284 return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC);285}286 287/*288 * Returns number of supported C-states of CPU core cpu289 * Negativ in error case290 * Zero if cpuidle does not export any C-states291 */292unsigned int cpuidle_state_count(unsigned int cpu)293{294 char file[SYSFS_PATH_MAX];295 struct stat statbuf;296 int idlestates = 1;297 298 299 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");300 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))301 return 0;302 303 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);304 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))305 return 0;306 307 while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {308 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU309 "cpu%u/cpuidle/state%d", cpu, idlestates);310 idlestates++;311 }312 idlestates--;313 return idlestates;314}315 316/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/317 318/*319 * helper function to read file from /sys into given buffer320 * fname is a relative path under "cpu/cpuidle/" dir321 */322static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,323 size_t buflen)324{325 char path[SYSFS_PATH_MAX];326 327 snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);328 329 return cpupower_read_sysfs(path, buf, buflen);330}331 332 333 334/* read access to files which contain one string */335 336enum cpuidle_string {337 CPUIDLE_GOVERNOR,338 CPUIDLE_GOVERNOR_RO,339 CPUIDLE_DRIVER,340 MAX_CPUIDLE_STRING_FILES341};342 343static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {344 [CPUIDLE_GOVERNOR] = "current_governor",345 [CPUIDLE_GOVERNOR_RO] = "current_governor_ro",346 [CPUIDLE_DRIVER] = "current_driver",347};348 349 350static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)351{352 char linebuf[MAX_LINE_LEN];353 char *result;354 unsigned int len;355 356 if (which >= MAX_CPUIDLE_STRING_FILES)357 return NULL;358 359 len = sysfs_cpuidle_read_file(cpuidle_string_files[which],360 linebuf, sizeof(linebuf));361 if (len == 0)362 return NULL;363 364 result = strdup(linebuf);365 if (result == NULL)366 return NULL;367 368 if (result[strlen(result) - 1] == '\n')369 result[strlen(result) - 1] = '\0';370 371 return result;372}373 374char *cpuidle_get_governor(void)375{376 char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);377 if (!tmp)378 return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);379 else380 return tmp;381}382 383char *cpuidle_get_driver(void)384{385 return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);386}387/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */388