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 19 unsigned 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 */ 48 int 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 || value < 0) 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 */ 104 unsigned 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 */ 125 unsigned 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 */ 157 static 158 unsigned 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 187 enum idlestate_value { 188 IDLESTATE_USAGE, 189 IDLESTATE_POWER, 190 IDLESTATE_LATENCY, 191 IDLESTATE_TIME, 192 IDLESTATE_DISABLE, 193 MAX_IDLESTATE_VALUE_FILES 194 }; 195 196 static 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 204 static 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 232 enum idlestate_string { 233 IDLESTATE_DESC, 234 IDLESTATE_NAME, 235 MAX_IDLESTATE_STRING_FILES 236 }; 237 238 static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = { 239 [IDLESTATE_DESC] = "desc", 240 [IDLESTATE_NAME] = "name", 241 }; 242 243 244 static 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 */ 278 int 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 */ 299 int 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 322 unsigned 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 328 unsigned 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 334 unsigned 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 340 char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate) 341 { 342 return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME); 343 } 344 345 char *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 */ 355 unsigned 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 -ENODEV; 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 */ 385 static 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 399 enum cpuidle_string { 400 CPUIDLE_GOVERNOR, 401 CPUIDLE_GOVERNOR_RO, 402 CPUIDLE_DRIVER, 403 MAX_CPUIDLE_STRING_FILES 404 }; 405 406 static 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 413 static 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 437 char *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 446 char *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 */ 458 int 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 */ 469 int sysfs_set_sched(const char *smt_mc, int val) 470 { 471 return -ENODEV; 472 } 473