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

cpufreq: Handle sorted frequency tables more efficiently

cpufreq drivers aren't required to provide a sorted frequency table
today, and even the ones which provide a sorted table aren't handled
efficiently by cpufreq core.

This patch adds infrastructure to verify if the freq-table provided by
the drivers is sorted or not, and use efficient helpers if they are
sorted.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Viresh Kumar and committed by
Rafael J. Wysocki
da0c6dc0 8d540ea7

+296 -11
+65 -8
drivers/cpufreq/freq_table.c
··· 113 113 } 114 114 EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify); 115 115 116 - int cpufreq_frequency_table_target(struct cpufreq_policy *policy, 117 - unsigned int target_freq, 118 - unsigned int relation) 116 + int cpufreq_table_index_unsorted(struct cpufreq_policy *policy, 117 + unsigned int target_freq, 118 + unsigned int relation) 119 119 { 120 120 struct cpufreq_frequency_table optimal = { 121 121 .driver_data = ~0, ··· 205 205 table[index].frequency); 206 206 return index; 207 207 } 208 - EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target); 208 + EXPORT_SYMBOL_GPL(cpufreq_table_index_unsorted); 209 209 210 210 int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy, 211 211 unsigned int freq) ··· 297 297 }; 298 298 EXPORT_SYMBOL_GPL(cpufreq_generic_attr); 299 299 300 + static int set_freq_table_sorted(struct cpufreq_policy *policy) 301 + { 302 + struct cpufreq_frequency_table *pos, *table = policy->freq_table; 303 + struct cpufreq_frequency_table *prev = NULL; 304 + int ascending = 0; 305 + 306 + policy->freq_table_sorted = CPUFREQ_TABLE_UNSORTED; 307 + 308 + cpufreq_for_each_valid_entry(pos, table) { 309 + if (!prev) { 310 + prev = pos; 311 + continue; 312 + } 313 + 314 + if (pos->frequency == prev->frequency) { 315 + pr_warn("Duplicate freq-table entries: %u\n", 316 + pos->frequency); 317 + return -EINVAL; 318 + } 319 + 320 + /* Frequency increased from prev to pos */ 321 + if (pos->frequency > prev->frequency) { 322 + /* But frequency was decreasing earlier */ 323 + if (ascending < 0) { 324 + pr_debug("Freq table is unsorted\n"); 325 + return 0; 326 + } 327 + 328 + ascending++; 329 + } else { 330 + /* Frequency decreased from prev to pos */ 331 + 332 + /* But frequency was increasing earlier */ 333 + if (ascending > 0) { 334 + pr_debug("Freq table is unsorted\n"); 335 + return 0; 336 + } 337 + 338 + ascending--; 339 + } 340 + 341 + prev = pos; 342 + } 343 + 344 + if (ascending > 0) 345 + policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_ASCENDING; 346 + else 347 + policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_DESCENDING; 348 + 349 + pr_debug("Freq table is sorted in %s order\n", 350 + ascending > 0 ? "ascending" : "descending"); 351 + 352 + return 0; 353 + } 354 + 300 355 int cpufreq_table_validate_and_show(struct cpufreq_policy *policy, 301 356 struct cpufreq_frequency_table *table) 302 357 { 303 - int ret = cpufreq_frequency_table_cpuinfo(policy, table); 358 + int ret; 304 359 305 - if (!ret) 306 - policy->freq_table = table; 360 + ret = cpufreq_frequency_table_cpuinfo(policy, table); 361 + if (ret) 362 + return ret; 307 363 308 - return ret; 364 + policy->freq_table = table; 365 + return set_freq_table_sorted(policy); 309 366 } 310 367 EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show); 311 368
+231 -3
include/linux/cpufreq.h
··· 36 36 37 37 struct cpufreq_governor; 38 38 39 + enum cpufreq_table_sorting { 40 + CPUFREQ_TABLE_UNSORTED, 41 + CPUFREQ_TABLE_SORTED_ASCENDING, 42 + CPUFREQ_TABLE_SORTED_DESCENDING 43 + }; 44 + 39 45 struct cpufreq_freqs { 40 46 unsigned int cpu; /* cpu nr */ 41 47 unsigned int old; ··· 93 87 94 88 struct cpufreq_user_policy user_policy; 95 89 struct cpufreq_frequency_table *freq_table; 90 + enum cpufreq_table_sorting freq_table_sorted; 96 91 97 92 struct list_head policy_list; 98 93 struct kobject kobj; ··· 604 597 struct cpufreq_frequency_table *table); 605 598 int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy); 606 599 607 - int cpufreq_frequency_table_target(struct cpufreq_policy *policy, 608 - unsigned int target_freq, 609 - unsigned int relation); 600 + int cpufreq_table_index_unsorted(struct cpufreq_policy *policy, 601 + unsigned int target_freq, 602 + unsigned int relation); 610 603 int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy, 611 604 unsigned int freq); 612 605 ··· 617 610 int cpufreq_boost_enabled(void); 618 611 int cpufreq_enable_boost_support(void); 619 612 bool policy_has_boost_freq(struct cpufreq_policy *policy); 613 + 614 + /* Find lowest freq at or above target in a table in ascending order */ 615 + static inline int cpufreq_table_find_index_al(struct cpufreq_policy *policy, 616 + unsigned int target_freq) 617 + { 618 + struct cpufreq_frequency_table *table = policy->freq_table; 619 + unsigned int freq; 620 + int i, best = -1; 621 + 622 + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { 623 + freq = table[i].frequency; 624 + 625 + if (freq >= target_freq) 626 + return i; 627 + 628 + best = i; 629 + } 630 + 631 + return best; 632 + } 633 + 634 + /* Find lowest freq at or above target in a table in descending order */ 635 + static inline int cpufreq_table_find_index_dl(struct cpufreq_policy *policy, 636 + unsigned int target_freq) 637 + { 638 + struct cpufreq_frequency_table *table = policy->freq_table; 639 + unsigned int freq; 640 + int i, best = -1; 641 + 642 + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { 643 + freq = table[i].frequency; 644 + 645 + if (freq == target_freq) 646 + return i; 647 + 648 + if (freq > target_freq) { 649 + best = i; 650 + continue; 651 + } 652 + 653 + /* No freq found above target_freq */ 654 + if (best == -1) 655 + return i; 656 + 657 + return best; 658 + } 659 + 660 + return best; 661 + } 662 + 663 + /* Works only on sorted freq-tables */ 664 + static inline int cpufreq_table_find_index_l(struct cpufreq_policy *policy, 665 + unsigned int target_freq) 666 + { 667 + target_freq = clamp_val(target_freq, policy->min, policy->max); 668 + 669 + if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING) 670 + return cpufreq_table_find_index_al(policy, target_freq); 671 + else 672 + return cpufreq_table_find_index_dl(policy, target_freq); 673 + } 674 + 675 + /* Find highest freq at or below target in a table in ascending order */ 676 + static inline int cpufreq_table_find_index_ah(struct cpufreq_policy *policy, 677 + unsigned int target_freq) 678 + { 679 + struct cpufreq_frequency_table *table = policy->freq_table; 680 + unsigned int freq; 681 + int i, best = -1; 682 + 683 + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { 684 + freq = table[i].frequency; 685 + 686 + if (freq == target_freq) 687 + return i; 688 + 689 + if (freq < target_freq) { 690 + best = i; 691 + continue; 692 + } 693 + 694 + /* No freq found below target_freq */ 695 + if (best == -1) 696 + return i; 697 + 698 + return best; 699 + } 700 + 701 + return best; 702 + } 703 + 704 + /* Find highest freq at or below target in a table in descending order */ 705 + static inline int cpufreq_table_find_index_dh(struct cpufreq_policy *policy, 706 + unsigned int target_freq) 707 + { 708 + struct cpufreq_frequency_table *table = policy->freq_table; 709 + unsigned int freq; 710 + int i, best = -1; 711 + 712 + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { 713 + freq = table[i].frequency; 714 + 715 + if (freq <= target_freq) 716 + return i; 717 + 718 + best = i; 719 + } 720 + 721 + return best; 722 + } 723 + 724 + /* Works only on sorted freq-tables */ 725 + static inline int cpufreq_table_find_index_h(struct cpufreq_policy *policy, 726 + unsigned int target_freq) 727 + { 728 + target_freq = clamp_val(target_freq, policy->min, policy->max); 729 + 730 + if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING) 731 + return cpufreq_table_find_index_ah(policy, target_freq); 732 + else 733 + return cpufreq_table_find_index_dh(policy, target_freq); 734 + } 735 + 736 + /* Find closest freq to target in a table in ascending order */ 737 + static inline int cpufreq_table_find_index_ac(struct cpufreq_policy *policy, 738 + unsigned int target_freq) 739 + { 740 + struct cpufreq_frequency_table *table = policy->freq_table; 741 + unsigned int freq; 742 + int i, best = -1; 743 + 744 + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { 745 + freq = table[i].frequency; 746 + 747 + if (freq == target_freq) 748 + return i; 749 + 750 + if (freq < target_freq) { 751 + best = i; 752 + continue; 753 + } 754 + 755 + /* No freq found below target_freq */ 756 + if (best == -1) 757 + return i; 758 + 759 + /* Choose the closest freq */ 760 + if (target_freq - table[best].frequency > freq - target_freq) 761 + return i; 762 + 763 + return best; 764 + } 765 + 766 + return best; 767 + } 768 + 769 + /* Find closest freq to target in a table in descending order */ 770 + static inline int cpufreq_table_find_index_dc(struct cpufreq_policy *policy, 771 + unsigned int target_freq) 772 + { 773 + struct cpufreq_frequency_table *table = policy->freq_table; 774 + unsigned int freq; 775 + int i, best = -1; 776 + 777 + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { 778 + freq = table[i].frequency; 779 + 780 + if (freq == target_freq) 781 + return i; 782 + 783 + if (freq > target_freq) { 784 + best = i; 785 + continue; 786 + } 787 + 788 + /* No freq found above target_freq */ 789 + if (best == -1) 790 + return i; 791 + 792 + /* Choose the closest freq */ 793 + if (table[best].frequency - target_freq > target_freq - freq) 794 + return i; 795 + 796 + return best; 797 + } 798 + 799 + return best; 800 + } 801 + 802 + /* Works only on sorted freq-tables */ 803 + static inline int cpufreq_table_find_index_c(struct cpufreq_policy *policy, 804 + unsigned int target_freq) 805 + { 806 + target_freq = clamp_val(target_freq, policy->min, policy->max); 807 + 808 + if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING) 809 + return cpufreq_table_find_index_ac(policy, target_freq); 810 + else 811 + return cpufreq_table_find_index_dc(policy, target_freq); 812 + } 813 + 814 + static inline int cpufreq_frequency_table_target(struct cpufreq_policy *policy, 815 + unsigned int target_freq, 816 + unsigned int relation) 817 + { 818 + if (unlikely(policy->freq_table_sorted == CPUFREQ_TABLE_UNSORTED)) 819 + return cpufreq_table_index_unsorted(policy, target_freq, 820 + relation); 821 + 822 + switch (relation) { 823 + case CPUFREQ_RELATION_L: 824 + return cpufreq_table_find_index_l(policy, target_freq); 825 + case CPUFREQ_RELATION_H: 826 + return cpufreq_table_find_index_h(policy, target_freq); 827 + case CPUFREQ_RELATION_C: 828 + return cpufreq_table_find_index_c(policy, target_freq); 829 + default: 830 + pr_err("%s: Invalid relation: %d\n", __func__, relation); 831 + return -EINVAL; 832 + } 833 + } 620 834 #else 621 835 static inline int cpufreq_boost_trigger_state(int state) 622 836 {