1 /* 2 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> 3 * (C) 2011 Thomas Renninger <trenn@novell.com> Novell Inc. 4 * 5 * Licensed under the terms of the GNU GPL License version 2. 6 */ 7 8 #include <stdio.h> 9 #include <errno.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/types.h> 13 #include <sys/stat.h> 14 #include <fcntl.h> 15 #include <unistd.h> 16 17 #include "cpuidle.h" 18 #include "cpupower_intern.h" 19 20 /* 21 * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir 22 * exists. 23 * For example the functionality to disable c-states was introduced in later 24 * kernel versions, this function can be used to explicitly check for this 25 * feature. 26 * 27 * returns 1 if the file exists, 0 otherwise. 28 */ 29 static 30 unsigned int cpuidle_state_file_exists(unsigned int cpu, 31 unsigned int idlestate, 32 const char *fname) 33 { 34 char path[SYSFS_PATH_MAX]; 35 struct stat statbuf; 36 37 38 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", 39 cpu, idlestate, fname); 40 if (stat(path, &statbuf) != 0) 41 return 0; 42 return 1; 43 } 44 45 /* 46 * helper function to read file from /sys into given buffer 47 * fname is a relative path under "cpuX/cpuidle/stateX/" dir 48 * cstates starting with 0, C0 is not counted as cstate. 49 * This means if you want C1 info, pass 0 as idlestate param 50 */ 51 static 52 unsigned int cpuidle_state_read_file(unsigned int cpu, 53 unsigned int idlestate, 54 const char *fname, char *buf, 55 size_t buflen) 56 { 57 char path[SYSFS_PATH_MAX]; 58 int fd; 59 ssize_t numread; 60 61 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", 62 cpu, idlestate, fname); 63 64 fd = open(path, O_RDONLY); 65 if (fd == -1) 66 return 0; 67 68 numread = read(fd, buf, buflen - 1); 69 if (numread < 1) { 70 close(fd); 71 return 0; 72 } 73 74 buf[numread] = '\0'; 75 close(fd); 76 77 return (unsigned int) numread; 78 } 79 80 /* 81 * helper function to write a new value to a /sys file 82 * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir 83 * 84 * Returns the number of bytes written or 0 on error 85 */ 86 static 87 unsigned int cpuidle_state_write_file(unsigned int cpu, 88 unsigned int idlestate, 89 const char *fname, 90 const char *value, size_t len) 91 { 92 char path[SYSFS_PATH_MAX]; 93 int fd; 94 ssize_t numwrite; 95 96 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", 97 cpu, idlestate, fname); 98 99 fd = open(path, O_WRONLY); 100 if (fd == -1) 101 return 0; 102 103 numwrite = write(fd, value, len); 104 if (numwrite < 1) { 105 close(fd); 106 return 0; 107 } 108 109 close(fd); 110 111 return (unsigned int) numwrite; 112 } 113 114 /* read access to files which contain one numeric value */ 115 116 enum idlestate_value { 117 IDLESTATE_USAGE, 118 IDLESTATE_POWER, 119 IDLESTATE_LATENCY, 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_TIME] = "time", 130 [IDLESTATE_DISABLE] = "disable", 131 }; 132 133 static 134 unsigned long long cpuidle_state_get_one_value(unsigned int cpu, 135 unsigned int idlestate, 136 enum idlestate_value which) 137 { 138 unsigned long long value; 139 unsigned int len; 140 char linebuf[MAX_LINE_LEN]; 141 char *endp; 142 143 if (which >= MAX_IDLESTATE_VALUE_FILES) 144 return 0; 145 146 len = cpuidle_state_read_file(cpu, idlestate, 147 idlestate_value_files[which], 148 linebuf, sizeof(linebuf)); 149 if (len == 0) 150 return 0; 151 152 value = strtoull(linebuf, &endp, 0); 153 154 if (endp == linebuf || errno == ERANGE) 155 return 0; 156 157 return value; 158 } 159 160 /* read access to files which contain one string */ 161 162 enum idlestate_string { 163 IDLESTATE_DESC, 164 IDLESTATE_NAME, 165 MAX_IDLESTATE_STRING_FILES 166 }; 167 168 static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = { 169 [IDLESTATE_DESC] = "desc", 170 [IDLESTATE_NAME] = "name", 171 }; 172 173 174 static char *cpuidle_state_get_one_string(unsigned int cpu, 175 unsigned int idlestate, 176 enum idlestate_string which) 177 { 178 char linebuf[MAX_LINE_LEN]; 179 char *result; 180 unsigned int len; 181 182 if (which >= MAX_IDLESTATE_STRING_FILES) 183 return NULL; 184 185 len = cpuidle_state_read_file(cpu, idlestate, 186 idlestate_string_files[which], 187 linebuf, sizeof(linebuf)); 188 if (len == 0) 189 return NULL; 190 191 result = strdup(linebuf); 192 if (result == NULL) 193 return NULL; 194 195 if (result[strlen(result) - 1] == '\n') 196 result[strlen(result) - 1] = '\0'; 197 198 return result; 199 } 200 201 /* 202 * Returns: 203 * 1 if disabled 204 * 0 if enabled 205 * -1 if idlestate is not available 206 * -2 if disabling is not supported by the kernel 207 */ 208 int cpuidle_is_state_disabled(unsigned int cpu, 209 unsigned int idlestate) 210 { 211 if (cpuidle_state_count(cpu) <= idlestate) 212 return -1; 213 214 if (!cpuidle_state_file_exists(cpu, idlestate, 215 idlestate_value_files[IDLESTATE_DISABLE])) 216 return -2; 217 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE); 218 } 219 220 /* 221 * Pass 1 as last argument to disable or 0 to enable the state 222 * Returns: 223 * 0 on success 224 * negative values on error, for example: 225 * -1 if idlestate is not available 226 * -2 if disabling is not supported by the kernel 227 * -3 No write access to disable/enable C-states 228 */ 229 int cpuidle_state_disable(unsigned int cpu, 230 unsigned int idlestate, 231 unsigned int disable) 232 { 233 char value[SYSFS_PATH_MAX]; 234 int bytes_written; 235 236 if (cpuidle_state_count(cpu) <= idlestate) 237 return -1; 238 239 if (!cpuidle_state_file_exists(cpu, idlestate, 240 idlestate_value_files[IDLESTATE_DISABLE])) 241 return -2; 242 243 snprintf(value, SYSFS_PATH_MAX, "%u", disable); 244 245 bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable", 246 value, sizeof(disable)); 247 if (bytes_written) 248 return 0; 249 return -3; 250 } 251 252 unsigned long cpuidle_state_latency(unsigned int cpu, 253 unsigned int idlestate) 254 { 255 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); 256 } 257 258 unsigned long cpuidle_state_usage(unsigned int cpu, 259 unsigned int idlestate) 260 { 261 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE); 262 } 263 264 unsigned long long cpuidle_state_time(unsigned int cpu, 265 unsigned int idlestate) 266 { 267 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME); 268 } 269 270 char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate) 271 { 272 return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME); 273 } 274 275 char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate) 276 { 277 return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC); 278 } 279 280 /* 281 * Returns number of supported C-states of CPU core cpu 282 * Negativ in error case 283 * Zero if cpuidle does not export any C-states 284 */ 285 unsigned int cpuidle_state_count(unsigned int cpu) 286 { 287 char file[SYSFS_PATH_MAX]; 288 struct stat statbuf; 289 int idlestates = 1; 290 291 292 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle"); 293 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) 294 return 0; 295 296 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu); 297 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) 298 return 0; 299 300 while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { 301 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU 302 "cpu%u/cpuidle/state%d", cpu, idlestates); 303 idlestates++; 304 } 305 idlestates--; 306 return idlestates; 307 } 308 309 /* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/ 310 311 /* 312 * helper function to read file from /sys into given buffer 313 * fname is a relative path under "cpu/cpuidle/" dir 314 */ 315 static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf, 316 size_t buflen) 317 { 318 char path[SYSFS_PATH_MAX]; 319 320 snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname); 321 322 return sysfs_read_file(path, buf, buflen); 323 } 324 325 326 327 /* read access to files which contain one string */ 328 329 enum cpuidle_string { 330 CPUIDLE_GOVERNOR, 331 CPUIDLE_GOVERNOR_RO, 332 CPUIDLE_DRIVER, 333 MAX_CPUIDLE_STRING_FILES 334 }; 335 336 static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = { 337 [CPUIDLE_GOVERNOR] = "current_governor", 338 [CPUIDLE_GOVERNOR_RO] = "current_governor_ro", 339 [CPUIDLE_DRIVER] = "current_driver", 340 }; 341 342 343 static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which) 344 { 345 char linebuf[MAX_LINE_LEN]; 346 char *result; 347 unsigned int len; 348 349 if (which >= MAX_CPUIDLE_STRING_FILES) 350 return NULL; 351 352 len = sysfs_cpuidle_read_file(cpuidle_string_files[which], 353 linebuf, sizeof(linebuf)); 354 if (len == 0) 355 return NULL; 356 357 result = strdup(linebuf); 358 if (result == NULL) 359 return NULL; 360 361 if (result[strlen(result) - 1] == '\n') 362 result[strlen(result) - 1] = '\0'; 363 364 return result; 365 } 366 367 char *cpuidle_get_governor(void) 368 { 369 char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO); 370 if (!tmp) 371 return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR); 372 else 373 return tmp; 374 } 375 376 char *cpuidle_get_driver(void) 377 { 378 return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER); 379 } 380 /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ 381