652 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>4 */5 6 7#include <unistd.h>8#include <stdio.h>9#include <errno.h>10#include <stdlib.h>11#include <string.h>12#include <limits.h>13 14#include <getopt.h>15 16#include "cpufreq.h"17#include "helpers/sysfs.h"18#include "helpers/helpers.h"19#include "helpers/bitmask.h"20 21#define LINE_LEN 1022 23static unsigned int count_cpus(void)24{25 FILE *fp;26 char value[LINE_LEN];27 unsigned int ret = 0;28 unsigned int cpunr = 0;29 30 fp = fopen("/proc/stat", "r");31 if (!fp) {32 printf(_("Couldn't count the number of CPUs (%s: %s), assuming 1\n"), "/proc/stat", strerror(errno));33 return 1;34 }35 36 while (!feof(fp)) {37 if (!fgets(value, LINE_LEN, fp))38 continue;39 value[LINE_LEN - 1] = '\0';40 if (strlen(value) < (LINE_LEN - 2))41 continue;42 if (strstr(value, "cpu "))43 continue;44 if (sscanf(value, "cpu%d ", &cpunr) != 1)45 continue;46 if (cpunr > ret)47 ret = cpunr;48 }49 fclose(fp);50 51 /* cpu count starts from 0, on error return 1 (UP) */52 return ret + 1;53}54 55 56static void proc_cpufreq_output(void)57{58 unsigned int cpu, nr_cpus;59 struct cpufreq_policy *policy;60 unsigned int min_pctg = 0;61 unsigned int max_pctg = 0;62 unsigned long min, max;63 64 printf(_(" minimum CPU frequency - maximum CPU frequency - governor\n"));65 66 nr_cpus = count_cpus();67 for (cpu = 0; cpu < nr_cpus; cpu++) {68 policy = cpufreq_get_policy(cpu);69 if (!policy)70 continue;71 72 if (cpufreq_get_hardware_limits(cpu, &min, &max)) {73 max = 0;74 } else {75 min_pctg = (policy->min * 100) / max;76 max_pctg = (policy->max * 100) / max;77 }78 printf("CPU%3d %9lu kHz (%3d %%) - %9lu kHz (%3d %%) - %s\n",79 cpu , policy->min, max ? min_pctg : 0, policy->max,80 max ? max_pctg : 0, policy->governor);81 82 cpufreq_put_policy(policy);83 }84}85 86static int no_rounding;87static void print_duration(unsigned long duration)88{89 unsigned long tmp;90 91 if (no_rounding) {92 if (duration > 1000000)93 printf("%u.%06u ms", ((unsigned int) duration/1000000),94 ((unsigned int) duration%1000000));95 else if (duration > 100000)96 printf("%u us", ((unsigned int) duration/1000));97 else if (duration > 1000)98 printf("%u.%03u us", ((unsigned int) duration/1000),99 ((unsigned int) duration%1000));100 else101 printf("%lu ns", duration);102 } else {103 if (duration > 1000000) {104 tmp = duration%10000;105 if (tmp >= 5000)106 duration += 10000;107 printf("%u.%02u ms", ((unsigned int) duration/1000000),108 ((unsigned int) (duration%1000000)/10000));109 } else if (duration > 100000) {110 tmp = duration%1000;111 if (tmp >= 500)112 duration += 1000;113 printf("%u us", ((unsigned int) duration / 1000));114 } else if (duration > 1000) {115 tmp = duration%100;116 if (tmp >= 50)117 duration += 100;118 printf("%u.%01u us", ((unsigned int) duration/1000),119 ((unsigned int) (duration%1000)/100));120 } else121 printf("%lu ns", duration);122 }123 return;124}125 126static int get_boost_mode_x86(unsigned int cpu)127{128 int support, active, b_states = 0, ret, pstate_no, i;129 /* ToDo: Make this more global */130 unsigned long pstates[MAX_HW_PSTATES] = {0,};131 132 ret = cpufreq_has_boost_support(cpu, &support, &active, &b_states);133 if (ret) {134 printf(_("Error while evaluating Boost Capabilities"135 " on CPU %d -- are you root?\n"), cpu);136 return ret;137 }138 /* P state changes via MSR are identified via cpuid 80000007139 on Intel and AMD, but we assume boost capable machines can do that140 if (cpuid_eax(0x80000000) >= 0x80000007141 && (cpuid_edx(0x80000007) & (1 << 7)))142 */143 144 printf(_(" boost state support:\n"));145 146 printf(_(" Supported: %s\n"), support ? _("yes") : _("no"));147 printf(_(" Active: %s\n"), active ? _("yes") : _("no"));148 149 if (cpupower_cpu_info.vendor == X86_VENDOR_AMD &&150 cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE) {151 return 0;152 } else if ((cpupower_cpu_info.vendor == X86_VENDOR_AMD &&153 cpupower_cpu_info.family >= 0x10) ||154 cpupower_cpu_info.vendor == X86_VENDOR_HYGON) {155 ret = decode_pstates(cpu, b_states, pstates, &pstate_no);156 if (ret)157 return ret;158 159 printf(_(" Boost States: %d\n"), b_states);160 printf(_(" Total States: %d\n"), pstate_no);161 for (i = 0; i < pstate_no; i++) {162 if (!pstates[i])163 continue;164 if (i < b_states)165 printf(_(" Pstate-Pb%d: %luMHz (boost state)"166 "\n"), i, pstates[i]);167 else168 printf(_(" Pstate-P%d: %luMHz\n"),169 i - b_states, pstates[i]);170 }171 } else if (cpupower_cpu_info.caps & CPUPOWER_CAP_HAS_TURBO_RATIO) {172 double bclk;173 unsigned long long intel_turbo_ratio = 0;174 unsigned int ratio;175 176 /* Any way to autodetect this ? */177 if (cpupower_cpu_info.caps & CPUPOWER_CAP_IS_SNB)178 bclk = 100.00;179 else180 bclk = 133.33;181 intel_turbo_ratio = msr_intel_get_turbo_ratio(cpu);182 dprint (" Ratio: 0x%llx - bclk: %f\n",183 intel_turbo_ratio, bclk);184 185 ratio = (intel_turbo_ratio >> 24) & 0xFF;186 if (ratio)187 printf(_(" %.0f MHz max turbo 4 active cores\n"),188 ratio * bclk);189 190 ratio = (intel_turbo_ratio >> 16) & 0xFF;191 if (ratio)192 printf(_(" %.0f MHz max turbo 3 active cores\n"),193 ratio * bclk);194 195 ratio = (intel_turbo_ratio >> 8) & 0xFF;196 if (ratio)197 printf(_(" %.0f MHz max turbo 2 active cores\n"),198 ratio * bclk);199 200 ratio = (intel_turbo_ratio >> 0) & 0xFF;201 if (ratio)202 printf(_(" %.0f MHz max turbo 1 active cores\n"),203 ratio * bclk);204 }205 return 0;206}207 208/* --boost / -b */209 210static int get_boost_mode(unsigned int cpu)211{212 struct cpufreq_available_frequencies *freqs;213 214 if (cpupower_cpu_info.vendor == X86_VENDOR_AMD ||215 cpupower_cpu_info.vendor == X86_VENDOR_HYGON ||216 cpupower_cpu_info.vendor == X86_VENDOR_INTEL)217 return get_boost_mode_x86(cpu);218 219 freqs = cpufreq_get_boost_frequencies(cpu);220 if (freqs) {221 printf(_(" boost frequency steps: "));222 while (freqs->next) {223 print_speed(freqs->frequency, no_rounding);224 printf(", ");225 freqs = freqs->next;226 }227 print_speed(freqs->frequency, no_rounding);228 printf("\n");229 cpufreq_put_available_frequencies(freqs);230 }231 232 return 0;233}234 235/* --freq / -f */236 237static int get_freq_kernel(unsigned int cpu, unsigned int human)238{239 unsigned long freq = cpufreq_get_freq_kernel(cpu);240 printf(_(" current CPU frequency: "));241 if (!freq) {242 printf(_(" Unable to call to kernel\n"));243 return -EINVAL;244 }245 if (human) {246 print_speed(freq, no_rounding);247 } else248 printf("%lu", freq);249 printf(_(" (asserted by call to kernel)\n"));250 return 0;251}252 253 254/* --hwfreq / -w */255 256static int get_freq_hardware(unsigned int cpu, unsigned int human)257{258 unsigned long freq = cpufreq_get_freq_hardware(cpu);259 printf(_(" current CPU frequency: "));260 if (!freq) {261 printf("Unable to call hardware\n");262 return -EINVAL;263 }264 if (human) {265 print_speed(freq, no_rounding);266 } else267 printf("%lu", freq);268 printf(_(" (asserted by call to hardware)\n"));269 return 0;270}271 272/* --hwlimits / -l */273 274static int get_hardware_limits(unsigned int cpu, unsigned int human)275{276 unsigned long min, max;277 278 if (cpufreq_get_hardware_limits(cpu, &min, &max)) {279 printf(_("Not Available\n"));280 return -EINVAL;281 }282 283 if (human) {284 printf(_(" hardware limits: "));285 print_speed(min, no_rounding);286 printf(" - ");287 print_speed(max, no_rounding);288 printf("\n");289 } else {290 printf("%lu %lu\n", min, max);291 }292 return 0;293}294 295/* --driver / -d */296 297static int get_driver(unsigned int cpu)298{299 char *driver = cpufreq_get_driver(cpu);300 if (!driver) {301 printf(_(" no or unknown cpufreq driver is active on this CPU\n"));302 return -EINVAL;303 }304 printf(" driver: %s\n", driver);305 cpufreq_put_driver(driver);306 return 0;307}308 309/* --policy / -p */310 311static int get_policy(unsigned int cpu)312{313 struct cpufreq_policy *policy = cpufreq_get_policy(cpu);314 if (!policy) {315 printf(_(" Unable to determine current policy\n"));316 return -EINVAL;317 }318 printf(_(" current policy: frequency should be within "));319 print_speed(policy->min, no_rounding);320 printf(_(" and "));321 print_speed(policy->max, no_rounding);322 323 printf(".\n ");324 printf(_("The governor \"%s\" may decide which speed to use\n"325 " within this range.\n"),326 policy->governor);327 cpufreq_put_policy(policy);328 return 0;329}330 331/* --governors / -g */332 333static int get_available_governors(unsigned int cpu)334{335 struct cpufreq_available_governors *governors =336 cpufreq_get_available_governors(cpu);337 338 printf(_(" available cpufreq governors: "));339 if (!governors) {340 printf(_("Not Available\n"));341 return -EINVAL;342 }343 344 while (governors->next) {345 printf("%s ", governors->governor);346 governors = governors->next;347 }348 printf("%s\n", governors->governor);349 cpufreq_put_available_governors(governors);350 return 0;351}352 353 354/* --affected-cpus / -a */355 356static int get_affected_cpus(unsigned int cpu)357{358 struct cpufreq_affected_cpus *cpus = cpufreq_get_affected_cpus(cpu);359 360 printf(_(" CPUs which need to have their frequency coordinated by software: "));361 if (!cpus) {362 printf(_("Not Available\n"));363 return -EINVAL;364 }365 366 while (cpus->next) {367 printf("%d ", cpus->cpu);368 cpus = cpus->next;369 }370 printf("%d\n", cpus->cpu);371 cpufreq_put_affected_cpus(cpus);372 return 0;373}374 375/* --related-cpus / -r */376 377static int get_related_cpus(unsigned int cpu)378{379 struct cpufreq_affected_cpus *cpus = cpufreq_get_related_cpus(cpu);380 381 printf(_(" CPUs which run at the same hardware frequency: "));382 if (!cpus) {383 printf(_("Not Available\n"));384 return -EINVAL;385 }386 387 while (cpus->next) {388 printf("%d ", cpus->cpu);389 cpus = cpus->next;390 }391 printf("%d\n", cpus->cpu);392 cpufreq_put_related_cpus(cpus);393 return 0;394}395 396/* --stats / -s */397 398static int get_freq_stats(unsigned int cpu, unsigned int human)399{400 unsigned long total_trans = cpufreq_get_transitions(cpu);401 unsigned long long total_time;402 struct cpufreq_stats *stats = cpufreq_get_stats(cpu, &total_time);403 while (stats) {404 if (human) {405 print_speed(stats->frequency, no_rounding);406 printf(":%.2f%%",407 (100.0 * stats->time_in_state) / total_time);408 } else409 printf("%lu:%llu",410 stats->frequency, stats->time_in_state);411 stats = stats->next;412 if (stats)413 printf(", ");414 }415 cpufreq_put_stats(stats);416 if (total_trans)417 printf(" (%lu)\n", total_trans);418 return 0;419}420 421/* --latency / -y */422 423static int get_latency(unsigned int cpu, unsigned int human)424{425 unsigned long latency = cpufreq_get_transition_latency(cpu);426 427 printf(_(" maximum transition latency: "));428 if (!latency || latency == UINT_MAX) {429 printf(_(" Cannot determine or is not supported.\n"));430 return -EINVAL;431 }432 433 if (human) {434 print_duration(latency);435 printf("\n");436 } else437 printf("%lu\n", latency);438 return 0;439}440 441/* --performance / -c */442 443static int get_perf_cap(unsigned int cpu)444{445 if (cpupower_cpu_info.vendor == X86_VENDOR_AMD &&446 cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE)447 amd_pstate_show_perf_and_freq(cpu, no_rounding);448 449 return 0;450}451 452static void debug_output_one(unsigned int cpu)453{454 struct cpufreq_available_frequencies *freqs;455 456 get_driver(cpu);457 get_related_cpus(cpu);458 get_affected_cpus(cpu);459 get_latency(cpu, 1);460 get_hardware_limits(cpu, 1);461 462 freqs = cpufreq_get_available_frequencies(cpu);463 if (freqs) {464 printf(_(" available frequency steps: "));465 while (freqs->next) {466 print_speed(freqs->frequency, no_rounding);467 printf(", ");468 freqs = freqs->next;469 }470 print_speed(freqs->frequency, no_rounding);471 printf("\n");472 cpufreq_put_available_frequencies(freqs);473 }474 475 get_available_governors(cpu);476 get_policy(cpu);477 if (get_freq_hardware(cpu, 1) < 0)478 get_freq_kernel(cpu, 1);479 get_boost_mode(cpu);480 get_perf_cap(cpu);481}482 483static struct option info_opts[] = {484 {"debug", no_argument, NULL, 'e'},485 {"boost", no_argument, NULL, 'b'},486 {"freq", no_argument, NULL, 'f'},487 {"hwfreq", no_argument, NULL, 'w'},488 {"hwlimits", no_argument, NULL, 'l'},489 {"driver", no_argument, NULL, 'd'},490 {"policy", no_argument, NULL, 'p'},491 {"governors", no_argument, NULL, 'g'},492 {"related-cpus", no_argument, NULL, 'r'},493 {"affected-cpus", no_argument, NULL, 'a'},494 {"stats", no_argument, NULL, 's'},495 {"latency", no_argument, NULL, 'y'},496 {"proc", no_argument, NULL, 'o'},497 {"human", no_argument, NULL, 'm'},498 {"no-rounding", no_argument, NULL, 'n'},499 {"performance", no_argument, NULL, 'c'},500 { },501};502 503int cmd_freq_info(int argc, char **argv)504{505 extern char *optarg;506 extern int optind, opterr, optopt;507 int ret = 0, cont = 1;508 unsigned int cpu = 0;509 unsigned int human = 0;510 int output_param = 0;511 512 do {513 ret = getopt_long(argc, argv, "oefwldpgrasmybnc", info_opts,514 NULL);515 switch (ret) {516 case '?':517 output_param = '?';518 cont = 0;519 break;520 case -1:521 cont = 0;522 break;523 case 'b':524 case 'o':525 case 'a':526 case 'r':527 case 'g':528 case 'p':529 case 'd':530 case 'l':531 case 'w':532 case 'f':533 case 'e':534 case 's':535 case 'y':536 case 'c':537 if (output_param) {538 output_param = -1;539 cont = 0;540 break;541 }542 output_param = ret;543 break;544 case 'm':545 if (human) {546 output_param = -1;547 cont = 0;548 break;549 }550 human = 1;551 break;552 case 'n':553 no_rounding = 1;554 break;555 default:556 fprintf(stderr, "invalid or unknown argument\n");557 return EXIT_FAILURE;558 }559 } while (cont);560 561 switch (output_param) {562 case 'o':563 if (!bitmask_isallclear(cpus_chosen)) {564 printf(_("The argument passed to this tool can't be "565 "combined with passing a --cpu argument\n"));566 return -EINVAL;567 }568 break;569 case 0:570 output_param = 'e';571 }572 573 ret = 0;574 575 /* Default is: show output of base_cpu only */576 if (bitmask_isallclear(cpus_chosen))577 bitmask_setbit(cpus_chosen, base_cpu);578 579 switch (output_param) {580 case -1:581 printf(_("You can't specify more than one --cpu parameter and/or\n"582 "more than one output-specific argument\n"));583 return -EINVAL;584 case '?':585 printf(_("invalid or unknown argument\n"));586 return -EINVAL;587 case 'o':588 proc_cpufreq_output();589 return EXIT_SUCCESS;590 }591 592 for (cpu = bitmask_first(cpus_chosen);593 cpu <= bitmask_last(cpus_chosen); cpu++) {594 595 if (!bitmask_isbitset(cpus_chosen, cpu))596 continue;597 598 printf(_("analyzing CPU %d:\n"), cpu);599 600 if (sysfs_is_cpu_online(cpu) != 1) {601 printf(_(" *is offline\n"));602 printf("\n");603 continue;604 }605 606 switch (output_param) {607 case 'b':608 get_boost_mode(cpu);609 break;610 case 'e':611 debug_output_one(cpu);612 break;613 case 'a':614 ret = get_affected_cpus(cpu);615 break;616 case 'r':617 ret = get_related_cpus(cpu);618 break;619 case 'g':620 ret = get_available_governors(cpu);621 break;622 case 'p':623 ret = get_policy(cpu);624 break;625 case 'd':626 ret = get_driver(cpu);627 break;628 case 'l':629 ret = get_hardware_limits(cpu, human);630 break;631 case 'w':632 ret = get_freq_hardware(cpu, human);633 break;634 case 'f':635 ret = get_freq_kernel(cpu, human);636 break;637 case 's':638 ret = get_freq_stats(cpu, human);639 break;640 case 'y':641 ret = get_latency(cpu, human);642 break;643 case 'c':644 ret = get_perf_cap(cpu);645 break;646 }647 if (ret)648 return ret;649 }650 return ret;651}652