Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.7-rc7 716 lines 15 kB view raw
1/* 2 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> 3 * 4 * Licensed under the terms of the GNU GPL License version 2. 5 */ 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 "cpufreq.h" 18#include "cpupower_intern.h" 19 20/* CPUFREQ sysfs access **************************************************/ 21 22/* helper function to read file from /sys into given buffer */ 23/* fname is a relative path under "cpuX/cpufreq" dir */ 24static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname, 25 char *buf, size_t buflen) 26{ 27 char path[SYSFS_PATH_MAX]; 28 29 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", 30 cpu, fname); 31 return sysfs_read_file(path, buf, buflen); 32} 33 34/* helper function to write a new value to a /sys file */ 35/* fname is a relative path under "cpuX/cpufreq" dir */ 36static unsigned int sysfs_cpufreq_write_file(unsigned int cpu, 37 const char *fname, 38 const char *value, size_t len) 39{ 40 char path[SYSFS_PATH_MAX]; 41 int fd; 42 ssize_t numwrite; 43 44 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", 45 cpu, fname); 46 47 fd = open(path, O_WRONLY); 48 if (fd == -1) 49 return 0; 50 51 numwrite = write(fd, value, len); 52 if (numwrite < 1) { 53 close(fd); 54 return 0; 55 } 56 57 close(fd); 58 59 return (unsigned int) numwrite; 60} 61 62/* read access to files which contain one numeric value */ 63 64enum cpufreq_value { 65 CPUINFO_CUR_FREQ, 66 CPUINFO_MIN_FREQ, 67 CPUINFO_MAX_FREQ, 68 CPUINFO_LATENCY, 69 SCALING_CUR_FREQ, 70 SCALING_MIN_FREQ, 71 SCALING_MAX_FREQ, 72 STATS_NUM_TRANSITIONS, 73 MAX_CPUFREQ_VALUE_READ_FILES 74}; 75 76static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = { 77 [CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq", 78 [CPUINFO_MIN_FREQ] = "cpuinfo_min_freq", 79 [CPUINFO_MAX_FREQ] = "cpuinfo_max_freq", 80 [CPUINFO_LATENCY] = "cpuinfo_transition_latency", 81 [SCALING_CUR_FREQ] = "scaling_cur_freq", 82 [SCALING_MIN_FREQ] = "scaling_min_freq", 83 [SCALING_MAX_FREQ] = "scaling_max_freq", 84 [STATS_NUM_TRANSITIONS] = "stats/total_trans" 85}; 86 87 88static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu, 89 enum cpufreq_value which) 90{ 91 unsigned long value; 92 unsigned int len; 93 char linebuf[MAX_LINE_LEN]; 94 char *endp; 95 96 if (which >= MAX_CPUFREQ_VALUE_READ_FILES) 97 return 0; 98 99 len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which], 100 linebuf, sizeof(linebuf)); 101 102 if (len == 0) 103 return 0; 104 105 value = strtoul(linebuf, &endp, 0); 106 107 if (endp == linebuf || errno == ERANGE) 108 return 0; 109 110 return value; 111} 112 113/* read access to files which contain one string */ 114 115enum cpufreq_string { 116 SCALING_DRIVER, 117 SCALING_GOVERNOR, 118 MAX_CPUFREQ_STRING_FILES 119}; 120 121static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = { 122 [SCALING_DRIVER] = "scaling_driver", 123 [SCALING_GOVERNOR] = "scaling_governor", 124}; 125 126 127static char *sysfs_cpufreq_get_one_string(unsigned int cpu, 128 enum cpufreq_string which) 129{ 130 char linebuf[MAX_LINE_LEN]; 131 char *result; 132 unsigned int len; 133 134 if (which >= MAX_CPUFREQ_STRING_FILES) 135 return NULL; 136 137 len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which], 138 linebuf, sizeof(linebuf)); 139 if (len == 0) 140 return NULL; 141 142 result = strdup(linebuf); 143 if (result == NULL) 144 return NULL; 145 146 if (result[strlen(result) - 1] == '\n') 147 result[strlen(result) - 1] = '\0'; 148 149 return result; 150} 151 152/* write access */ 153 154enum cpufreq_write { 155 WRITE_SCALING_MIN_FREQ, 156 WRITE_SCALING_MAX_FREQ, 157 WRITE_SCALING_GOVERNOR, 158 WRITE_SCALING_SET_SPEED, 159 MAX_CPUFREQ_WRITE_FILES 160}; 161 162static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = { 163 [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq", 164 [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq", 165 [WRITE_SCALING_GOVERNOR] = "scaling_governor", 166 [WRITE_SCALING_SET_SPEED] = "scaling_setspeed", 167}; 168 169static int sysfs_cpufreq_write_one_value(unsigned int cpu, 170 enum cpufreq_write which, 171 const char *new_value, size_t len) 172{ 173 if (which >= MAX_CPUFREQ_WRITE_FILES) 174 return 0; 175 176 if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which], 177 new_value, len) != len) 178 return -ENODEV; 179 180 return 0; 181}; 182 183unsigned long cpufreq_get_freq_kernel(unsigned int cpu) 184{ 185 return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ); 186} 187 188unsigned long cpufreq_get_freq_hardware(unsigned int cpu) 189{ 190 return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ); 191} 192 193unsigned long cpufreq_get_transition_latency(unsigned int cpu) 194{ 195 return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY); 196} 197 198int cpufreq_get_hardware_limits(unsigned int cpu, 199 unsigned long *min, 200 unsigned long *max) 201{ 202 if ((!min) || (!max)) 203 return -EINVAL; 204 205 *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ); 206 if (!*min) 207 return -ENODEV; 208 209 *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ); 210 if (!*max) 211 return -ENODEV; 212 213 return 0; 214} 215 216char *cpufreq_get_driver(unsigned int cpu) 217{ 218 return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER); 219} 220 221void cpufreq_put_driver(char *ptr) 222{ 223 if (!ptr) 224 return; 225 free(ptr); 226} 227 228struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu) 229{ 230 struct cpufreq_policy *policy; 231 232 policy = malloc(sizeof(struct cpufreq_policy)); 233 if (!policy) 234 return NULL; 235 236 policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR); 237 if (!policy->governor) { 238 free(policy); 239 return NULL; 240 } 241 policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); 242 policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ); 243 if ((!policy->min) || (!policy->max)) { 244 free(policy->governor); 245 free(policy); 246 return NULL; 247 } 248 249 return policy; 250} 251 252void cpufreq_put_policy(struct cpufreq_policy *policy) 253{ 254 if ((!policy) || (!policy->governor)) 255 return; 256 257 free(policy->governor); 258 policy->governor = NULL; 259 free(policy); 260} 261 262struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned 263 int cpu) 264{ 265 struct cpufreq_available_governors *first = NULL; 266 struct cpufreq_available_governors *current = NULL; 267 char linebuf[MAX_LINE_LEN]; 268 unsigned int pos, i; 269 unsigned int len; 270 271 len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors", 272 linebuf, sizeof(linebuf)); 273 if (len == 0) 274 return NULL; 275 276 pos = 0; 277 for (i = 0; i < len; i++) { 278 if (linebuf[i] == ' ' || linebuf[i] == '\n') { 279 if (i - pos < 2) 280 continue; 281 if (current) { 282 current->next = malloc(sizeof(*current)); 283 if (!current->next) 284 goto error_out; 285 current = current->next; 286 } else { 287 first = malloc(sizeof(*first)); 288 if (!first) 289 goto error_out; 290 current = first; 291 } 292 current->first = first; 293 current->next = NULL; 294 295 current->governor = malloc(i - pos + 1); 296 if (!current->governor) 297 goto error_out; 298 299 memcpy(current->governor, linebuf + pos, i - pos); 300 current->governor[i - pos] = '\0'; 301 pos = i + 1; 302 } 303 } 304 305 return first; 306 307 error_out: 308 while (first) { 309 current = first->next; 310 if (first->governor) 311 free(first->governor); 312 free(first); 313 first = current; 314 } 315 return NULL; 316} 317 318void cpufreq_put_available_governors(struct cpufreq_available_governors *any) 319{ 320 struct cpufreq_available_governors *tmp, *next; 321 322 if (!any) 323 return; 324 325 tmp = any->first; 326 while (tmp) { 327 next = tmp->next; 328 if (tmp->governor) 329 free(tmp->governor); 330 free(tmp); 331 tmp = next; 332 } 333} 334 335 336struct cpufreq_available_frequencies 337*cpufreq_get_available_frequencies(unsigned int cpu) 338{ 339 struct cpufreq_available_frequencies *first = NULL; 340 struct cpufreq_available_frequencies *current = NULL; 341 char one_value[SYSFS_PATH_MAX]; 342 char linebuf[MAX_LINE_LEN]; 343 unsigned int pos, i; 344 unsigned int len; 345 346 len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies", 347 linebuf, sizeof(linebuf)); 348 if (len == 0) 349 return NULL; 350 351 pos = 0; 352 for (i = 0; i < len; i++) { 353 if (linebuf[i] == ' ' || linebuf[i] == '\n') { 354 if (i - pos < 2) 355 continue; 356 if (i - pos >= SYSFS_PATH_MAX) 357 goto error_out; 358 if (current) { 359 current->next = malloc(sizeof(*current)); 360 if (!current->next) 361 goto error_out; 362 current = current->next; 363 } else { 364 first = malloc(sizeof(*first)); 365 if (!first) 366 goto error_out; 367 current = first; 368 } 369 current->first = first; 370 current->next = NULL; 371 372 memcpy(one_value, linebuf + pos, i - pos); 373 one_value[i - pos] = '\0'; 374 if (sscanf(one_value, "%lu", &current->frequency) != 1) 375 goto error_out; 376 377 pos = i + 1; 378 } 379 } 380 381 return first; 382 383 error_out: 384 while (first) { 385 current = first->next; 386 free(first); 387 first = current; 388 } 389 return NULL; 390} 391 392void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies 393 *any) { 394 struct cpufreq_available_frequencies *tmp, *next; 395 396 if (!any) 397 return; 398 399 tmp = any->first; 400 while (tmp) { 401 next = tmp->next; 402 free(tmp); 403 tmp = next; 404 } 405} 406 407static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu, 408 const char *file) 409{ 410 struct cpufreq_affected_cpus *first = NULL; 411 struct cpufreq_affected_cpus *current = NULL; 412 char one_value[SYSFS_PATH_MAX]; 413 char linebuf[MAX_LINE_LEN]; 414 unsigned int pos, i; 415 unsigned int len; 416 417 len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf)); 418 if (len == 0) 419 return NULL; 420 421 pos = 0; 422 for (i = 0; i < len; i++) { 423 if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') { 424 if (i - pos < 1) 425 continue; 426 if (i - pos >= SYSFS_PATH_MAX) 427 goto error_out; 428 if (current) { 429 current->next = malloc(sizeof(*current)); 430 if (!current->next) 431 goto error_out; 432 current = current->next; 433 } else { 434 first = malloc(sizeof(*first)); 435 if (!first) 436 goto error_out; 437 current = first; 438 } 439 current->first = first; 440 current->next = NULL; 441 442 memcpy(one_value, linebuf + pos, i - pos); 443 one_value[i - pos] = '\0'; 444 445 if (sscanf(one_value, "%u", &current->cpu) != 1) 446 goto error_out; 447 448 pos = i + 1; 449 } 450 } 451 452 return first; 453 454 error_out: 455 while (first) { 456 current = first->next; 457 free(first); 458 first = current; 459 } 460 return NULL; 461} 462 463struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu) 464{ 465 return sysfs_get_cpu_list(cpu, "affected_cpus"); 466} 467 468void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any) 469{ 470 struct cpufreq_affected_cpus *tmp, *next; 471 472 if (!any) 473 return; 474 475 tmp = any->first; 476 while (tmp) { 477 next = tmp->next; 478 free(tmp); 479 tmp = next; 480 } 481} 482 483 484struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu) 485{ 486 return sysfs_get_cpu_list(cpu, "related_cpus"); 487} 488 489void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any) 490{ 491 cpufreq_put_affected_cpus(any); 492} 493 494static int verify_gov(char *new_gov, char *passed_gov) 495{ 496 unsigned int i, j = 0; 497 498 if (!passed_gov || (strlen(passed_gov) > 19)) 499 return -EINVAL; 500 501 strncpy(new_gov, passed_gov, 20); 502 for (i = 0; i < 20; i++) { 503 if (j) { 504 new_gov[i] = '\0'; 505 continue; 506 } 507 if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z')) 508 continue; 509 510 if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z')) 511 continue; 512 513 if (new_gov[i] == '-') 514 continue; 515 516 if (new_gov[i] == '_') 517 continue; 518 519 if (new_gov[i] == '\0') { 520 j = 1; 521 continue; 522 } 523 return -EINVAL; 524 } 525 new_gov[19] = '\0'; 526 return 0; 527} 528 529int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy) 530{ 531 char min[SYSFS_PATH_MAX]; 532 char max[SYSFS_PATH_MAX]; 533 char gov[SYSFS_PATH_MAX]; 534 int ret; 535 unsigned long old_min; 536 int write_max_first; 537 538 if (!policy || !(policy->governor)) 539 return -EINVAL; 540 541 if (policy->max < policy->min) 542 return -EINVAL; 543 544 if (verify_gov(gov, policy->governor)) 545 return -EINVAL; 546 547 snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min); 548 snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max); 549 550 old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); 551 write_max_first = (old_min && (policy->max < old_min) ? 0 : 1); 552 553 if (write_max_first) { 554 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, 555 max, strlen(max)); 556 if (ret) 557 return ret; 558 } 559 560 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min, 561 strlen(min)); 562 if (ret) 563 return ret; 564 565 if (!write_max_first) { 566 ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, 567 max, strlen(max)); 568 if (ret) 569 return ret; 570 } 571 572 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, 573 gov, strlen(gov)); 574} 575 576 577int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq) 578{ 579 char value[SYSFS_PATH_MAX]; 580 581 snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq); 582 583 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, 584 value, strlen(value)); 585} 586 587 588int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq) 589{ 590 char value[SYSFS_PATH_MAX]; 591 592 snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq); 593 594 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, 595 value, strlen(value)); 596} 597 598int cpufreq_modify_policy_governor(unsigned int cpu, char *governor) 599{ 600 char new_gov[SYSFS_PATH_MAX]; 601 602 if ((!governor) || (strlen(governor) > 19)) 603 return -EINVAL; 604 605 if (verify_gov(new_gov, governor)) 606 return -EINVAL; 607 608 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, 609 new_gov, strlen(new_gov)); 610} 611 612int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency) 613{ 614 struct cpufreq_policy *pol = cpufreq_get_policy(cpu); 615 char userspace_gov[] = "userspace"; 616 char freq[SYSFS_PATH_MAX]; 617 int ret; 618 619 if (!pol) 620 return -ENODEV; 621 622 if (strncmp(pol->governor, userspace_gov, 9) != 0) { 623 ret = cpufreq_modify_policy_governor(cpu, userspace_gov); 624 if (ret) { 625 cpufreq_put_policy(pol); 626 return ret; 627 } 628 } 629 630 cpufreq_put_policy(pol); 631 632 snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency); 633 634 return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED, 635 freq, strlen(freq)); 636} 637 638struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu, 639 unsigned long long *total_time) 640{ 641 struct cpufreq_stats *first = NULL; 642 struct cpufreq_stats *current = NULL; 643 char one_value[SYSFS_PATH_MAX]; 644 char linebuf[MAX_LINE_LEN]; 645 unsigned int pos, i; 646 unsigned int len; 647 648 len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state", 649 linebuf, sizeof(linebuf)); 650 if (len == 0) 651 return NULL; 652 653 *total_time = 0; 654 pos = 0; 655 for (i = 0; i < len; i++) { 656 if (i == strlen(linebuf) || linebuf[i] == '\n') { 657 if (i - pos < 2) 658 continue; 659 if ((i - pos) >= SYSFS_PATH_MAX) 660 goto error_out; 661 if (current) { 662 current->next = malloc(sizeof(*current)); 663 if (!current->next) 664 goto error_out; 665 current = current->next; 666 } else { 667 first = malloc(sizeof(*first)); 668 if (!first) 669 goto error_out; 670 current = first; 671 } 672 current->first = first; 673 current->next = NULL; 674 675 memcpy(one_value, linebuf + pos, i - pos); 676 one_value[i - pos] = '\0'; 677 if (sscanf(one_value, "%lu %llu", 678 &current->frequency, 679 &current->time_in_state) != 2) 680 goto error_out; 681 682 *total_time = *total_time + current->time_in_state; 683 pos = i + 1; 684 } 685 } 686 687 return first; 688 689 error_out: 690 while (first) { 691 current = first->next; 692 free(first); 693 first = current; 694 } 695 return NULL; 696} 697 698void cpufreq_put_stats(struct cpufreq_stats *any) 699{ 700 struct cpufreq_stats *tmp, *next; 701 702 if (!any) 703 return; 704 705 tmp = any->first; 706 while (tmp) { 707 next = tmp->next; 708 free(tmp); 709 tmp = next; 710 } 711} 712 713unsigned long cpufreq_get_transitions(unsigned int cpu) 714{ 715 return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS); 716}