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