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