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