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 "helpers/sysfs.h" 18 19unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen) 20{ 21 int fd; 22 ssize_t numread; 23 24 fd = open(path, O_RDONLY); 25 if (fd == -1) 26 return 0; 27 28 numread = read(fd, buf, buflen - 1); 29 if (numread < 1) { 30 close(fd); 31 return 0; 32 } 33 34 buf[numread] = '\0'; 35 close(fd); 36 37 return (unsigned int) numread; 38} 39 40/* 41 * Detect whether a CPU is online 42 * 43 * Returns: 44 * 1 -> if CPU is online 45 * 0 -> if CPU is offline 46 * negative errno values in error case 47 */ 48int sysfs_is_cpu_online(unsigned int cpu) 49{ 50 char path[SYSFS_PATH_MAX]; 51 int fd; 52 ssize_t numread; 53 unsigned long long value; 54 char linebuf[MAX_LINE_LEN]; 55 char *endp; 56 struct stat statbuf; 57 58 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu); 59 60 if (stat(path, &statbuf) != 0) 61 return 0; 62 63 /* 64 * kernel without CONFIG_HOTPLUG_CPU 65 * -> cpuX directory exists, but not cpuX/online file 66 */ 67 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu); 68 if (stat(path, &statbuf) != 0) 69 return 1; 70 71 fd = open(path, O_RDONLY); 72 if (fd == -1) 73 return -errno; 74 75 numread = read(fd, linebuf, MAX_LINE_LEN - 1); 76 if (numread < 1) { 77 close(fd); 78 return -EIO; 79 } 80 linebuf[numread] = '\0'; 81 close(fd); 82 83 value = strtoull(linebuf, &endp, 0); 84 if (value > 1) 85 return -EINVAL; 86 87 return value; 88} 89 90/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ 91 92 93/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ 94 95/* 96 * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir 97 * exists. 98 * For example the functionality to disable c-states was introduced in later 99 * kernel versions, this function can be used to explicitly check for this 100 * feature. 101 * 102 * returns 1 if the file exists, 0 otherwise. 103 */ 104unsigned int sysfs_idlestate_file_exists(unsigned int cpu, 105 unsigned int idlestate, 106 const char *fname) 107{ 108 char path[SYSFS_PATH_MAX]; 109 struct stat statbuf; 110 111 112 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", 113 cpu, idlestate, fname); 114 if (stat(path, &statbuf) != 0) 115 return 0; 116 return 1; 117} 118 119/* 120 * helper function to read file from /sys into given buffer 121 * fname is a relative path under "cpuX/cpuidle/stateX/" dir 122 * cstates starting with 0, C0 is not counted as cstate. 123 * This means if you want C1 info, pass 0 as idlestate param 124 */ 125unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate, 126 const char *fname, char *buf, size_t buflen) 127{ 128 char path[SYSFS_PATH_MAX]; 129 int fd; 130 ssize_t numread; 131 132 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", 133 cpu, idlestate, fname); 134 135 fd = open(path, O_RDONLY); 136 if (fd == -1) 137 return 0; 138 139 numread = read(fd, buf, buflen - 1); 140 if (numread < 1) { 141 close(fd); 142 return 0; 143 } 144 145 buf[numread] = '\0'; 146 close(fd); 147 148 return (unsigned int) numread; 149} 150 151/* 152 * helper function to write a new value to a /sys file 153 * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir 154 * 155 * Returns the number of bytes written or 0 on error 156 */ 157static 158unsigned int sysfs_idlestate_write_file(unsigned int cpu, 159 unsigned int idlestate, 160 const char *fname, 161 const char *value, size_t len) 162{ 163 char path[SYSFS_PATH_MAX]; 164 int fd; 165 ssize_t numwrite; 166 167 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", 168 cpu, idlestate, fname); 169 170 fd = open(path, O_WRONLY); 171 if (fd == -1) 172 return 0; 173 174 numwrite = write(fd, value, len); 175 if (numwrite < 1) { 176 close(fd); 177 return 0; 178 } 179 180 close(fd); 181 182 return (unsigned int) numwrite; 183} 184 185/* read access to files which contain one numeric value */ 186 187enum idlestate_value { 188 IDLESTATE_USAGE, 189 IDLESTATE_POWER, 190 IDLESTATE_LATENCY, 191 IDLESTATE_TIME, 192 IDLESTATE_DISABLE, 193 MAX_IDLESTATE_VALUE_FILES 194}; 195 196static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = { 197 [IDLESTATE_USAGE] = "usage", 198 [IDLESTATE_POWER] = "power", 199 [IDLESTATE_LATENCY] = "latency", 200 [IDLESTATE_TIME] = "time", 201 [IDLESTATE_DISABLE] = "disable", 202}; 203 204static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu, 205 unsigned int idlestate, 206 enum idlestate_value which) 207{ 208 unsigned long long value; 209 unsigned int len; 210 char linebuf[MAX_LINE_LEN]; 211 char *endp; 212 213 if (which >= MAX_IDLESTATE_VALUE_FILES) 214 return 0; 215 216 len = sysfs_idlestate_read_file(cpu, idlestate, 217 idlestate_value_files[which], 218 linebuf, sizeof(linebuf)); 219 if (len == 0) 220 return 0; 221 222 value = strtoull(linebuf, &endp, 0); 223 224 if (endp == linebuf || errno == ERANGE) 225 return 0; 226 227 return value; 228} 229 230/* read access to files which contain one string */ 231 232enum idlestate_string { 233 IDLESTATE_DESC, 234 IDLESTATE_NAME, 235 MAX_IDLESTATE_STRING_FILES 236}; 237 238static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = { 239 [IDLESTATE_DESC] = "desc", 240 [IDLESTATE_NAME] = "name", 241}; 242 243 244static char *sysfs_idlestate_get_one_string(unsigned int cpu, 245 unsigned int idlestate, 246 enum idlestate_string which) 247{ 248 char linebuf[MAX_LINE_LEN]; 249 char *result; 250 unsigned int len; 251 252 if (which >= MAX_IDLESTATE_STRING_FILES) 253 return NULL; 254 255 len = sysfs_idlestate_read_file(cpu, idlestate, 256 idlestate_string_files[which], 257 linebuf, sizeof(linebuf)); 258 if (len == 0) 259 return NULL; 260 261 result = strdup(linebuf); 262 if (result == NULL) 263 return NULL; 264 265 if (result[strlen(result) - 1] == '\n') 266 result[strlen(result) - 1] = '\0'; 267 268 return result; 269} 270 271/* 272 * Returns: 273 * 1 if disabled 274 * 0 if enabled 275 * -1 if idlestate is not available 276 * -2 if disabling is not supported by the kernel 277 */ 278int sysfs_is_idlestate_disabled(unsigned int cpu, 279 unsigned int idlestate) 280{ 281 if (sysfs_get_idlestate_count(cpu) <= idlestate) 282 return -1; 283 284 if (!sysfs_idlestate_file_exists(cpu, idlestate, 285 idlestate_value_files[IDLESTATE_DISABLE])) 286 return -2; 287 return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_DISABLE); 288} 289 290/* 291 * Pass 1 as last argument to disable or 0 to enable the state 292 * Returns: 293 * 0 on success 294 * negative values on error, for example: 295 * -1 if idlestate is not available 296 * -2 if disabling is not supported by the kernel 297 * -3 No write access to disable/enable C-states 298 */ 299int sysfs_idlestate_disable(unsigned int cpu, 300 unsigned int idlestate, 301 unsigned int disable) 302{ 303 char value[SYSFS_PATH_MAX]; 304 int bytes_written; 305 306 if (sysfs_get_idlestate_count(cpu) <= idlestate) 307 return -1; 308 309 if (!sysfs_idlestate_file_exists(cpu, idlestate, 310 idlestate_value_files[IDLESTATE_DISABLE])) 311 return -2; 312 313 snprintf(value, SYSFS_PATH_MAX, "%u", disable); 314 315 bytes_written = sysfs_idlestate_write_file(cpu, idlestate, "disable", 316 value, sizeof(disable)); 317 if (bytes_written) 318 return 0; 319 return -3; 320} 321 322unsigned long sysfs_get_idlestate_latency(unsigned int cpu, 323 unsigned int idlestate) 324{ 325 return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); 326} 327 328unsigned long sysfs_get_idlestate_usage(unsigned int cpu, 329 unsigned int idlestate) 330{ 331 return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE); 332} 333 334unsigned long long sysfs_get_idlestate_time(unsigned int cpu, 335 unsigned int idlestate) 336{ 337 return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME); 338} 339 340char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate) 341{ 342 return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME); 343} 344 345char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate) 346{ 347 return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC); 348} 349 350/* 351 * Returns number of supported C-states of CPU core cpu 352 * Negativ in error case 353 * Zero if cpuidle does not export any C-states 354 */ 355unsigned int sysfs_get_idlestate_count(unsigned int cpu) 356{ 357 char file[SYSFS_PATH_MAX]; 358 struct stat statbuf; 359 int idlestates = 1; 360 361 362 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle"); 363 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) 364 return 0; 365 366 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu); 367 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) 368 return 0; 369 370 while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { 371 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU 372 "cpu%u/cpuidle/state%d", cpu, idlestates); 373 idlestates++; 374 } 375 idlestates--; 376 return idlestates; 377} 378 379/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/ 380 381/* 382 * helper function to read file from /sys into given buffer 383 * fname is a relative path under "cpu/cpuidle/" dir 384 */ 385static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf, 386 size_t buflen) 387{ 388 char path[SYSFS_PATH_MAX]; 389 390 snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname); 391 392 return sysfs_read_file(path, buf, buflen); 393} 394 395 396 397/* read access to files which contain one string */ 398 399enum cpuidle_string { 400 CPUIDLE_GOVERNOR, 401 CPUIDLE_GOVERNOR_RO, 402 CPUIDLE_DRIVER, 403 MAX_CPUIDLE_STRING_FILES 404}; 405 406static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = { 407 [CPUIDLE_GOVERNOR] = "current_governor", 408 [CPUIDLE_GOVERNOR_RO] = "current_governor_ro", 409 [CPUIDLE_DRIVER] = "current_driver", 410}; 411 412 413static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which) 414{ 415 char linebuf[MAX_LINE_LEN]; 416 char *result; 417 unsigned int len; 418 419 if (which >= MAX_CPUIDLE_STRING_FILES) 420 return NULL; 421 422 len = sysfs_cpuidle_read_file(cpuidle_string_files[which], 423 linebuf, sizeof(linebuf)); 424 if (len == 0) 425 return NULL; 426 427 result = strdup(linebuf); 428 if (result == NULL) 429 return NULL; 430 431 if (result[strlen(result) - 1] == '\n') 432 result[strlen(result) - 1] = '\0'; 433 434 return result; 435} 436 437char *sysfs_get_cpuidle_governor(void) 438{ 439 char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO); 440 if (!tmp) 441 return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR); 442 else 443 return tmp; 444} 445 446char *sysfs_get_cpuidle_driver(void) 447{ 448 return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER); 449} 450/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ 451 452/* 453 * Get sched_mc or sched_smt settings 454 * Pass "mc" or "smt" as argument 455 * 456 * Returns negative value on failure 457 */ 458int sysfs_get_sched(const char *smt_mc) 459{ 460 return -ENODEV; 461} 462 463/* 464 * Get sched_mc or sched_smt settings 465 * Pass "mc" or "smt" as argument 466 * 467 * Returns negative value on failure 468 */ 469int sysfs_set_sched(const char *smt_mc, int val) 470{ 471 return -ENODEV; 472} 473