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 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 253 unsigned 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 259 unsigned 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 265 unsigned 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 271 unsigned 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 277 char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate) 278 { 279 return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME); 280 } 281 282 char *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 cpu 289 * Negativ in error case 290 * Zero if cpuidle does not export any C-states 291 */ 292 unsigned 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_CPU 309 "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 buffer 320 * fname is a relative path under "cpu/cpuidle/" dir 321 */ 322 static 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 336 enum cpuidle_string { 337 CPUIDLE_GOVERNOR, 338 CPUIDLE_GOVERNOR_RO, 339 CPUIDLE_DRIVER, 340 MAX_CPUIDLE_STRING_FILES 341 }; 342 343 static 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 350 static 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 374 char *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 else 380 return tmp; 381 } 382 383 char *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