linux/tools/power/cpupower/lib/cpufreq.c
<<
>>
Prefs
   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 cpupower_read_sysfs(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_frequencies
 337*cpufreq_get_frequencies(const char *type, unsigned int cpu)
 338{
 339        struct cpufreq_frequencies *first = NULL;
 340        struct cpufreq_frequencies *current = NULL;
 341        char one_value[SYSFS_PATH_MAX];
 342        char linebuf[MAX_LINE_LEN];
 343        char fname[MAX_LINE_LEN];
 344        unsigned int pos, i;
 345        unsigned int len;
 346
 347        snprintf(fname, MAX_LINE_LEN, "scaling_%s_frequencies", type);
 348
 349        len = sysfs_cpufreq_read_file(cpu, fname,
 350                                linebuf, sizeof(linebuf));
 351        if (len == 0)
 352                return NULL;
 353
 354        pos = 0;
 355        for (i = 0; i < len; i++) {
 356                if (linebuf[i] == ' ' || linebuf[i] == '\n') {
 357                        if (i - pos < 2)
 358                                continue;
 359                        if (i - pos >= SYSFS_PATH_MAX)
 360                                goto error_out;
 361                        if (current) {
 362                                current->next = malloc(sizeof(*current));
 363                                if (!current->next)
 364                                        goto error_out;
 365                                current = current->next;
 366                        } else {
 367                                first = malloc(sizeof(*first));
 368                                if (!first)
 369                                        goto error_out;
 370                                current = first;
 371                        }
 372                        current->first = first;
 373                        current->next = NULL;
 374
 375                        memcpy(one_value, linebuf + pos, i - pos);
 376                        one_value[i - pos] = '\0';
 377                        if (sscanf(one_value, "%lu", &current->frequency) != 1)
 378                                goto error_out;
 379
 380                        pos = i + 1;
 381                }
 382        }
 383
 384        return first;
 385
 386 error_out:
 387        while (first) {
 388                current = first->next;
 389                free(first);
 390                first = current;
 391        }
 392        return NULL;
 393}
 394
 395void cpufreq_put_frequencies(struct cpufreq_frequencies *any)
 396{
 397        struct cpufreq_frequencies *tmp, *next;
 398
 399        if (!any)
 400                return;
 401
 402        tmp = any->first;
 403        while (tmp) {
 404                next = tmp->next;
 405                free(tmp);
 406                tmp = next;
 407        }
 408}
 409
 410static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,
 411                                                        const char *file)
 412{
 413        struct cpufreq_affected_cpus *first = NULL;
 414        struct cpufreq_affected_cpus *current = NULL;
 415        char one_value[SYSFS_PATH_MAX];
 416        char linebuf[MAX_LINE_LEN];
 417        unsigned int pos, i;
 418        unsigned int len;
 419
 420        len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));
 421        if (len == 0)
 422                return NULL;
 423
 424        pos = 0;
 425        for (i = 0; i < len; i++) {
 426                if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {
 427                        if (i - pos  < 1)
 428                                continue;
 429                        if (i - pos >= SYSFS_PATH_MAX)
 430                                goto error_out;
 431                        if (current) {
 432                                current->next = malloc(sizeof(*current));
 433                                if (!current->next)
 434                                        goto error_out;
 435                                current = current->next;
 436                        } else {
 437                                first = malloc(sizeof(*first));
 438                                if (!first)
 439                                        goto error_out;
 440                                current = first;
 441                        }
 442                        current->first = first;
 443                        current->next = NULL;
 444
 445                        memcpy(one_value, linebuf + pos, i - pos);
 446                        one_value[i - pos] = '\0';
 447
 448                        if (sscanf(one_value, "%u", &current->cpu) != 1)
 449                                goto error_out;
 450
 451                        pos = i + 1;
 452                }
 453        }
 454
 455        return first;
 456
 457 error_out:
 458        while (first) {
 459                current = first->next;
 460                free(first);
 461                first = current;
 462        }
 463        return NULL;
 464}
 465
 466struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu)
 467{
 468        return sysfs_get_cpu_list(cpu, "affected_cpus");
 469}
 470
 471void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any)
 472{
 473        struct cpufreq_affected_cpus *tmp, *next;
 474
 475        if (!any)
 476                return;
 477
 478        tmp = any->first;
 479        while (tmp) {
 480                next = tmp->next;
 481                free(tmp);
 482                tmp = next;
 483        }
 484}
 485
 486
 487struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu)
 488{
 489        return sysfs_get_cpu_list(cpu, "related_cpus");
 490}
 491
 492void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any)
 493{
 494        cpufreq_put_affected_cpus(any);
 495}
 496
 497static int verify_gov(char *new_gov, char *passed_gov)
 498{
 499        unsigned int i, j = 0;
 500
 501        if (!passed_gov || (strlen(passed_gov) > 19))
 502                return -EINVAL;
 503
 504        strncpy(new_gov, passed_gov, 20);
 505        for (i = 0; i < 20; i++) {
 506                if (j) {
 507                        new_gov[i] = '\0';
 508                        continue;
 509                }
 510                if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z'))
 511                        continue;
 512
 513                if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z'))
 514                        continue;
 515
 516                if (new_gov[i] == '-')
 517                        continue;
 518
 519                if (new_gov[i] == '_')
 520                        continue;
 521
 522                if (new_gov[i] == '\0') {
 523                        j = 1;
 524                        continue;
 525                }
 526                return -EINVAL;
 527        }
 528        new_gov[19] = '\0';
 529        return 0;
 530}
 531
 532int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy)
 533{
 534        char min[SYSFS_PATH_MAX];
 535        char max[SYSFS_PATH_MAX];
 536        char gov[SYSFS_PATH_MAX];
 537        int ret;
 538        unsigned long old_min;
 539        int write_max_first;
 540
 541        if (!policy || !(policy->governor))
 542                return -EINVAL;
 543
 544        if (policy->max < policy->min)
 545                return -EINVAL;
 546
 547        if (verify_gov(gov, policy->governor))
 548                return -EINVAL;
 549
 550        snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);
 551        snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);
 552
 553        old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
 554        write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);
 555
 556        if (write_max_first) {
 557                ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
 558                                                    max, strlen(max));
 559                if (ret)
 560                        return ret;
 561        }
 562
 563        ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
 564                                            strlen(min));
 565        if (ret)
 566                return ret;
 567
 568        if (!write_max_first) {
 569                ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
 570                                                    max, strlen(max));
 571                if (ret)
 572                        return ret;
 573        }
 574
 575        return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
 576                                             gov, strlen(gov));
 577}
 578
 579
 580int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq)
 581{
 582        char value[SYSFS_PATH_MAX];
 583
 584        snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);
 585
 586        return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,
 587                                             value, strlen(value));
 588}
 589
 590
 591int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq)
 592{
 593        char value[SYSFS_PATH_MAX];
 594
 595        snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);
 596
 597        return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
 598                                             value, strlen(value));
 599}
 600
 601int cpufreq_modify_policy_governor(unsigned int cpu, char *governor)
 602{
 603        char new_gov[SYSFS_PATH_MAX];
 604
 605        if ((!governor) || (strlen(governor) > 19))
 606                return -EINVAL;
 607
 608        if (verify_gov(new_gov, governor))
 609                return -EINVAL;
 610
 611        return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
 612                                             new_gov, strlen(new_gov));
 613}
 614
 615int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency)
 616{
 617        struct cpufreq_policy *pol = cpufreq_get_policy(cpu);
 618        char userspace_gov[] = "userspace";
 619        char freq[SYSFS_PATH_MAX];
 620        int ret;
 621
 622        if (!pol)
 623                return -ENODEV;
 624
 625        if (strncmp(pol->governor, userspace_gov, 9) != 0) {
 626                ret = cpufreq_modify_policy_governor(cpu, userspace_gov);
 627                if (ret) {
 628                        cpufreq_put_policy(pol);
 629                        return ret;
 630                }
 631        }
 632
 633        cpufreq_put_policy(pol);
 634
 635        snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
 636
 637        return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
 638                                             freq, strlen(freq));
 639}
 640
 641struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu,
 642                                        unsigned long long *total_time)
 643{
 644        struct cpufreq_stats *first = NULL;
 645        struct cpufreq_stats *current = NULL;
 646        char one_value[SYSFS_PATH_MAX];
 647        char linebuf[MAX_LINE_LEN];
 648        unsigned int pos, i;
 649        unsigned int len;
 650
 651        len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
 652                                linebuf, sizeof(linebuf));
 653        if (len == 0)
 654                return NULL;
 655
 656        *total_time = 0;
 657        pos = 0;
 658        for (i = 0; i < len; i++) {
 659                if (i == strlen(linebuf) || linebuf[i] == '\n') {
 660                        if (i - pos < 2)
 661                                continue;
 662                        if ((i - pos) >= SYSFS_PATH_MAX)
 663                                goto error_out;
 664                        if (current) {
 665                                current->next = malloc(sizeof(*current));
 666                                if (!current->next)
 667                                        goto error_out;
 668                                current = current->next;
 669                        } else {
 670                                first = malloc(sizeof(*first));
 671                                if (!first)
 672                                        goto error_out;
 673                                current = first;
 674                        }
 675                        current->first = first;
 676                        current->next = NULL;
 677
 678                        memcpy(one_value, linebuf + pos, i - pos);
 679                        one_value[i - pos] = '\0';
 680                        if (sscanf(one_value, "%lu %llu",
 681                                        &current->frequency,
 682                                        &current->time_in_state) != 2)
 683                                goto error_out;
 684
 685                        *total_time = *total_time + current->time_in_state;
 686                        pos = i + 1;
 687                }
 688        }
 689
 690        return first;
 691
 692 error_out:
 693        while (first) {
 694                current = first->next;
 695                free(first);
 696                first = current;
 697        }
 698        return NULL;
 699}
 700
 701void cpufreq_put_stats(struct cpufreq_stats *any)
 702{
 703        struct cpufreq_stats *tmp, *next;
 704
 705        if (!any)
 706                return;
 707
 708        tmp = any->first;
 709        while (tmp) {
 710                next = tmp->next;
 711                free(tmp);
 712                tmp = next;
 713        }
 714}
 715
 716unsigned long cpufreq_get_transitions(unsigned int cpu)
 717{
 718        return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
 719}
 720