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_TIME, 120 IDLESTATE_DISABLE, 121 MAX_IDLESTATE_VALUE_FILES 122 }; 123 124 static 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 132 static 133 unsigned 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 161 enum idlestate_string { 162 IDLESTATE_DESC, 163 IDLESTATE_NAME, 164 MAX_IDLESTATE_STRING_FILES 165 }; 166 167 static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = { 168 [IDLESTATE_DESC] = "desc", 169 [IDLESTATE_NAME] = "name", 170 }; 171 172 173 static 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 */ 207 int 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 */ 228 int 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 251 unsigned 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 257 unsigned 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 263 unsigned 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 269 char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate) 270 { 271 return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME); 272 } 273 274 char *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 */ 284 unsigned 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 */ 314 static 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 328 enum cpuidle_string { 329 CPUIDLE_GOVERNOR, 330 CPUIDLE_GOVERNOR_RO, 331 CPUIDLE_DRIVER, 332 MAX_CPUIDLE_STRING_FILES 333 }; 334 335 static 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 342 static 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 366 char *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 375 char *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 */ 380
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.