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 */