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 */ 28 static 29 unsigned 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 */ 50 static 51 unsigned 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 */ 85 static 86 unsigned 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 115 enum 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 125 static 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 134 static 135 unsigned 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 163 enum idlestate_string { 164 IDLESTATE_DESC, 165 IDLESTATE_NAME, 166 MAX_IDLESTATE_STRING_FILES 167 }; 168 169 static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = { 170 [IDLESTATE_DESC] = "desc", 171 [IDLESTATE_NAME] = "name", 172 }; 173 174 175 static 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 disabled 205 * 0 if enabled 206 * -1 if idlestate is not available 207 * -2 if disabling is not supported by the kernel 208 */ 209 int 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 state 223 * Returns: 224 * 0 on success 225 * negative values on error, for example: 226 * -1 if idlestate is not available 227 * -2 if disabling is not supported by the kernel 228 * -3 No write access to disable/enable C-states 229 */ 230 int 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 int len; 237 238 if (cpuidle_state_count(cpu) <= idlestate) 239 return -1; 240 241 if (!cpuidle_state_file_exists(cpu, idlestate, 242 idlestate_value_files[IDLESTATE_DISABLE])) 243 return -2; 244 245 len = snprintf(value, SYSFS_PATH_MAX, "%u", disable); 246 247 bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable", 248 value, len); 249 if (bytes_written) 250 return 0; 251 return -3; 252 } 253 254 unsigned long cpuidle_state_latency(unsigned int cpu, 255 unsigned int idlestate) 256 { 257 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); 258 } 259 260 unsigned long cpuidle_state_residency(unsigned int cpu, 261 unsigned int idlestate) 262 { 263 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_RESIDENCY); 264 } 265 266 unsigned long cpuidle_state_usage(unsigned int cpu, 267 unsigned int idlestate) 268 { 269 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE); 270 } 271 272 unsigned long long cpuidle_state_time(unsigned int cpu, 273 unsigned int idlestate) 274 { 275 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME); 276 } 277 278 char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate) 279 { 280 return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME); 281 } 282 283 char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate) 284 { 285 return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC); 286 } 287 288 /* 289 * Returns number of supported C-states of CPU core cpu 290 * Negativ in error case 291 * Zero if cpuidle does not export any C-states 292 */ 293 unsigned int cpuidle_state_count(unsigned int cpu) 294 { 295 char file[SYSFS_PATH_MAX]; 296 struct stat statbuf; 297 int idlestates = 1; 298 299 300 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle"); 301 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) 302 return 0; 303 304 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu); 305 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) 306 return 0; 307 308 while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { 309 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU 310 "cpu%u/cpuidle/state%d", cpu, idlestates); 311 idlestates++; 312 } 313 idlestates--; 314 return idlestates; 315 } 316 317 /* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/ 318 319 /* 320 * helper function to read file from /sys into given buffer 321 * fname is a relative path under "cpu/cpuidle/" dir 322 */ 323 static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf, 324 size_t buflen) 325 { 326 char path[SYSFS_PATH_MAX]; 327 328 snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname); 329 330 return cpupower_read_sysfs(path, buf, buflen); 331 } 332 333 334 335 /* read access to files which contain one string */ 336 337 enum cpuidle_string { 338 CPUIDLE_GOVERNOR, 339 CPUIDLE_GOVERNOR_RO, 340 CPUIDLE_DRIVER, 341 MAX_CPUIDLE_STRING_FILES 342 }; 343 344 static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = { 345 [CPUIDLE_GOVERNOR] = "current_governor", 346 [CPUIDLE_GOVERNOR_RO] = "current_governor_ro", 347 [CPUIDLE_DRIVER] = "current_driver", 348 }; 349 350 351 static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which) 352 { 353 char linebuf[MAX_LINE_LEN]; 354 char *result; 355 unsigned int len; 356 357 if (which >= MAX_CPUIDLE_STRING_FILES) 358 return NULL; 359 360 len = sysfs_cpuidle_read_file(cpuidle_string_files[which], 361 linebuf, sizeof(linebuf)); 362 if (len == 0) 363 return NULL; 364 365 result = strdup(linebuf); 366 if (result == NULL) 367 return NULL; 368 369 if (result[strlen(result) - 1] == '\n') 370 result[strlen(result) - 1] = '\0'; 371 372 return result; 373 } 374 375 char *cpuidle_get_governor(void) 376 { 377 char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO); 378 if (!tmp) 379 return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR); 380 else 381 return tmp; 382 } 383 384 char *cpuidle_get_driver(void) 385 { 386 return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER); 387 } 388 /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ 389