FILE
cpuidle.c
File preview with syntax highlighting.
linux-6.19.14/tools/power/cpupower/lib/cpuidle.c
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_RESIDENCY, 120 IDLESTATE_TIME, 121 IDLESTATE_DISABLE, 122 MAX_IDLESTATE_VALUE_FILES 123}; 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 134static 135unsigned 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 errno = 0; 154 value = strtoull(linebuf, &endp, 0); 155 156 if (endp == linebuf || errno == ERANGE) 157 return 0; 158 159 return value; 160} 161 162/* read access to files which contain one string */ 163 164enum idlestate_string { 165 IDLESTATE_DESC, 166 IDLESTATE_NAME, 167 MAX_IDLESTATE_STRING_FILES 168}; 169 170static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = { 171 [IDLESTATE_DESC] = "desc", 172 [IDLESTATE_NAME] = "name", 173}; 174 175 176static char *cpuidle_state_get_one_string(unsigned int cpu, 177 unsigned int idlestate, 178 enum idlestate_string which) 179{ 180 char linebuf[MAX_LINE_LEN]; 181 char *result; 182 unsigned int len; 183 184 if (which >= MAX_IDLESTATE_STRING_FILES) 185 return NULL; 186 187 len = cpuidle_state_read_file(cpu, idlestate, 188 idlestate_string_files[which], 189 linebuf, sizeof(linebuf)); 190 if (len == 0) 191 return NULL; 192 193 result = strdup(linebuf); 194 if (result == NULL) 195 return NULL; 196 197 if (result[strlen(result) - 1] == '\n') 198 result[strlen(result) - 1] = '\0'; 199 200 return result; 201} 202 203/* 204 * Returns: 205 * 1 if disabled 206 * 0 if enabled 207 * -1 if idlestate is not available 208 * -2 if disabling is not supported by the kernel 209 */ 210int cpuidle_is_state_disabled(unsigned int cpu, 211 unsigned int idlestate) 212{ 213 if (cpuidle_state_count(cpu) <= idlestate) 214 return -1; 215 216 if (!cpuidle_state_file_exists(cpu, idlestate, 217 idlestate_value_files[IDLESTATE_DISABLE])) 218 return -2; 219 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE); 220} 221 222/* 223 * Pass 1 as last argument to disable or 0 to enable the state 224 * Returns: 225 * 0 on success 226 * negative values on error, for example: 227 * -1 if idlestate is not available 228 * -2 if disabling is not supported by the kernel 229 * -3 No write access to disable/enable C-states 230 */ 231int cpuidle_state_disable(unsigned int cpu, 232 unsigned int idlestate, 233 unsigned int disable) 234{ 235 char value[SYSFS_PATH_MAX]; 236 int bytes_written; 237 int len; 238 239 if (cpuidle_state_count(cpu) <= idlestate) 240 return -1; 241 242 if (!cpuidle_state_file_exists(cpu, idlestate, 243 idlestate_value_files[IDLESTATE_DISABLE])) 244 return -2; 245 246 len = snprintf(value, SYSFS_PATH_MAX, "%u", disable); 247 248 bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable", 249 value, len); 250 if (bytes_written) 251 return 0; 252 return -3; 253} 254 255unsigned long cpuidle_state_latency(unsigned int cpu, 256 unsigned int idlestate) 257{ 258 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); 259} 260 261unsigned long cpuidle_state_residency(unsigned int cpu, 262 unsigned int idlestate) 263{ 264 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_RESIDENCY); 265} 266 267unsigned long cpuidle_state_usage(unsigned int cpu, 268 unsigned int idlestate) 269{ 270 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE); 271} 272 273unsigned long long cpuidle_state_time(unsigned int cpu, 274 unsigned int idlestate) 275{ 276 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME); 277} 278 279char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate) 280{ 281 return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME); 282} 283 284char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate) 285{ 286 return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC); 287} 288 289/* 290 * Returns number of supported C-states of CPU core cpu 291 * Negativ in error case 292 * Zero if cpuidle does not export any C-states 293 */ 294unsigned int cpuidle_state_count(unsigned int cpu) 295{ 296 char file[SYSFS_PATH_MAX]; 297 struct stat statbuf; 298 int idlestates = 1; 299 300 301 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle"); 302 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) 303 return 0; 304 305 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu); 306 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) 307 return 0; 308 309 while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { 310 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU 311 "cpu%u/cpuidle/state%d", cpu, idlestates); 312 idlestates++; 313 } 314 idlestates--; 315 return idlestates; 316} 317 318/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/ 319 320/* 321 * helper function to read file from /sys into given buffer 322 * fname is a relative path under "cpu/cpuidle/" dir 323 */ 324static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf, 325 size_t buflen) 326{ 327 char path[SYSFS_PATH_MAX]; 328 329 snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname); 330 331 return cpupower_read_sysfs(path, buf, buflen); 332} 333 334 335 336/* read access to files which contain one string */ 337 338enum cpuidle_string { 339 CPUIDLE_GOVERNOR, 340 CPUIDLE_GOVERNOR_RO, 341 CPUIDLE_DRIVER, 342 MAX_CPUIDLE_STRING_FILES 343}; 344 345static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = { 346 [CPUIDLE_GOVERNOR] = "current_governor", 347 [CPUIDLE_GOVERNOR_RO] = "current_governor_ro", 348 [CPUIDLE_DRIVER] = "current_driver", 349}; 350 351 352static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which) 353{ 354 char linebuf[MAX_LINE_LEN]; 355 char *result; 356 unsigned int len; 357 358 if (which >= MAX_CPUIDLE_STRING_FILES) 359 return NULL; 360 361 len = sysfs_cpuidle_read_file(cpuidle_string_files[which], 362 linebuf, sizeof(linebuf)); 363 if (len == 0) 364 return NULL; 365 366 result = strdup(linebuf); 367 if (result == NULL) 368 return NULL; 369 370 if (result[strlen(result) - 1] == '\n') 371 result[strlen(result) - 1] = '\0'; 372 373 return result; 374} 375 376char *cpuidle_get_governor(void) 377{ 378 char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO); 379 if (!tmp) 380 return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR); 381 else 382 return tmp; 383} 384 385char *cpuidle_get_driver(void) 386{ 387 return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER); 388} 389/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
~/
Read-only file preview.