linux/tools/power/cpupower/lib/sysfs.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#include <stdio.h>
   8#include <errno.h>
   9#include <stdlib.h>
  10#include <string.h>
  11#include <limits.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
  19#define PATH_TO_CPU "/sys/devices/system/cpu/"
  20#define MAX_LINE_LEN 4096
  21#define SYSFS_PATH_MAX 255
  22
  23
  24static unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
  25{
  26        int fd;
  27        ssize_t numread;
  28
  29        fd = open(path, O_RDONLY);
  30        if (fd == -1)
  31                return 0;
  32
  33        numread = read(fd, buf, buflen - 1);
  34        if (numread < 1) {
  35                close(fd);
  36                return 0;
  37        }
  38
  39        buf[numread] = '\0';
  40        close(fd);
  41
  42        return (unsigned int) numread;
  43}
  44
  45
  46/* CPUFREQ sysfs access **************************************************/
  47
  48/* helper function to read file from /sys into given buffer */
  49/* fname is a relative path under "cpuX/cpufreq" dir */
  50static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname,
  51                                            char *buf, size_t buflen)
  52{
  53        char path[SYSFS_PATH_MAX];
  54
  55        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
  56                         cpu, fname);
  57        return sysfs_read_file(path, buf, buflen);
  58}
  59
  60/* helper function to write a new value to a /sys file */
  61/* fname is a relative path under "cpuX/cpufreq" dir */
  62static unsigned int sysfs_cpufreq_write_file(unsigned int cpu,
  63                                             const char *fname,
  64                                             const char *value, size_t len)
  65{
  66        char path[SYSFS_PATH_MAX];
  67        int fd;
  68        ssize_t numwrite;
  69
  70        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
  71                         cpu, fname);
  72
  73        fd = open(path, O_WRONLY);
  74        if (fd == -1)
  75                return 0;
  76
  77        numwrite = write(fd, value, len);
  78        if (numwrite < 1) {
  79                close(fd);
  80                return 0;
  81        }
  82
  83        close(fd);
  84
  85        return (unsigned int) numwrite;
  86}
  87
  88/* read access to files which contain one numeric value */
  89
  90enum cpufreq_value {
  91        CPUINFO_CUR_FREQ,
  92        CPUINFO_MIN_FREQ,
  93        CPUINFO_MAX_FREQ,
  94        CPUINFO_LATENCY,
  95        SCALING_CUR_FREQ,
  96        SCALING_MIN_FREQ,
  97        SCALING_MAX_FREQ,
  98        STATS_NUM_TRANSITIONS,
  99        MAX_CPUFREQ_VALUE_READ_FILES
 100};
 101
 102static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = {
 103        [CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq",
 104        [CPUINFO_MIN_FREQ] = "cpuinfo_min_freq",
 105        [CPUINFO_MAX_FREQ] = "cpuinfo_max_freq",
 106        [CPUINFO_LATENCY]  = "cpuinfo_transition_latency",
 107        [SCALING_CUR_FREQ] = "scaling_cur_freq",
 108        [SCALING_MIN_FREQ] = "scaling_min_freq",
 109        [SCALING_MAX_FREQ] = "scaling_max_freq",
 110        [STATS_NUM_TRANSITIONS] = "stats/total_trans"
 111};
 112
 113
 114static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu,
 115                                                 enum cpufreq_value which)
 116{
 117        unsigned long value;
 118        unsigned int len;
 119        char linebuf[MAX_LINE_LEN];
 120        char *endp;
 121
 122        if (which >= MAX_CPUFREQ_VALUE_READ_FILES)
 123                return 0;
 124
 125        len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which],
 126                                linebuf, sizeof(linebuf));
 127
 128        if (len == 0)
 129                return 0;
 130
 131        value = strtoul(linebuf, &endp, 0);
 132
 133        if (endp == linebuf || errno == ERANGE)
 134                return 0;
 135
 136        return value;
 137}
 138
 139/* read access to files which contain one string */
 140
 141enum cpufreq_string {
 142        SCALING_DRIVER,
 143        SCALING_GOVERNOR,
 144        MAX_CPUFREQ_STRING_FILES
 145};
 146
 147static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
 148        [SCALING_DRIVER] = "scaling_driver",
 149        [SCALING_GOVERNOR] = "scaling_governor",
 150};
 151
 152
 153static char *sysfs_cpufreq_get_one_string(unsigned int cpu,
 154                                           enum cpufreq_string which)
 155{
 156        char linebuf[MAX_LINE_LEN];
 157        char *result;
 158        unsigned int len;
 159
 160        if (which >= MAX_CPUFREQ_STRING_FILES)
 161                return NULL;
 162
 163        len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
 164                                linebuf, sizeof(linebuf));
 165        if (len == 0)
 166                return NULL;
 167
 168        result = strdup(linebuf);
 169        if (result == NULL)
 170                return NULL;
 171
 172        if (result[strlen(result) - 1] == '\n')
 173                result[strlen(result) - 1] = '\0';
 174
 175        return result;
 176}
 177
 178/* write access */
 179
 180enum cpufreq_write {
 181        WRITE_SCALING_MIN_FREQ,
 182        WRITE_SCALING_MAX_FREQ,
 183        WRITE_SCALING_GOVERNOR,
 184        WRITE_SCALING_SET_SPEED,
 185        MAX_CPUFREQ_WRITE_FILES
 186};
 187
 188static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {
 189        [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",
 190        [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",
 191        [WRITE_SCALING_GOVERNOR] = "scaling_governor",
 192        [WRITE_SCALING_SET_SPEED] = "scaling_setspeed",
 193};
 194
 195static int sysfs_cpufreq_write_one_value(unsigned int cpu,
 196                                         enum cpufreq_write which,
 197                                         const char *new_value, size_t len)
 198{
 199        if (which >= MAX_CPUFREQ_WRITE_FILES)
 200                return 0;
 201
 202        if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
 203                                        new_value, len) != len)
 204                return -ENODEV;
 205
 206        return 0;
 207};
 208
 209unsigned long sysfs_get_freq_kernel(unsigned int cpu)
 210{
 211        return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
 212}
 213
 214unsigned long sysfs_get_freq_hardware(unsigned int cpu)
 215{
 216        return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
 217}
 218
 219unsigned long sysfs_get_freq_transition_latency(unsigned int cpu)
 220{
 221        return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
 222}
 223
 224int sysfs_get_freq_hardware_limits(unsigned int cpu,
 225                              unsigned long *min,
 226                              unsigned long *max)
 227{
 228        if ((!min) || (!max))
 229                return -EINVAL;
 230
 231        *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ);
 232        if (!*min)
 233                return -ENODEV;
 234
 235        *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);
 236        if (!*max)
 237                return -ENODEV;
 238
 239        return 0;
 240}
 241
 242char *sysfs_get_freq_driver(unsigned int cpu)
 243{
 244        return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
 245}
 246
 247struct cpufreq_policy *sysfs_get_freq_policy(unsigned int cpu)
 248{
 249        struct cpufreq_policy *policy;
 250
 251        policy = malloc(sizeof(struct cpufreq_policy));
 252        if (!policy)
 253                return NULL;
 254
 255        policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);
 256        if (!policy->governor) {
 257                free(policy);
 258                return NULL;
 259        }
 260        policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
 261        policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ);
 262        if ((!policy->min) || (!policy->max)) {
 263                free(policy->governor);
 264                free(policy);
 265                return NULL;
 266        }
 267
 268        return policy;
 269}
 270
 271struct cpufreq_available_governors *
 272sysfs_get_freq_available_governors(unsigned int cpu) {
 273        struct cpufreq_available_governors *first = NULL;
 274        struct cpufreq_available_governors *current = NULL;
 275        char linebuf[MAX_LINE_LEN];
 276        unsigned int pos, i;
 277        unsigned int len;
 278
 279        len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
 280                                linebuf, sizeof(linebuf));
 281        if (len == 0)
 282                return NULL;
 283
 284        pos = 0;
 285        for (i = 0; i < len; i++) {
 286                if (linebuf[i] == ' ' || linebuf[i] == '\n') {
 287                        if (i - pos < 2)
 288                                continue;
 289                        if (current) {
 290                                current->next = malloc(sizeof(*current));
 291                                if (!current->next)
 292                                        goto error_out;
 293                                current = current->next;
 294                        } else {
 295                                first = malloc(sizeof(*first));
 296                                if (!first)
 297                                        goto error_out;
 298                                current = first;
 299                        }
 300                        current->first = first;
 301                        current->next = NULL;
 302
 303                        current->governor = malloc(i - pos + 1);
 304                        if (!current->governor)
 305                                goto error_out;
 306
 307                        memcpy(current->governor, linebuf + pos, i - pos);
 308                        current->governor[i - pos] = '\0';
 309                        pos = i + 1;
 310                }
 311        }
 312
 313        return first;
 314
 315 error_out:
 316        while (first) {
 317                current = first->next;
 318                if (first->governor)
 319                        free(first->governor);
 320                free(first);
 321                first = current;
 322        }
 323        return NULL;
 324}
 325
 326
 327struct cpufreq_available_frequencies *
 328sysfs_get_available_frequencies(unsigned int cpu) {
 329        struct cpufreq_available_frequencies *first = NULL;
 330        struct cpufreq_available_frequencies *current = NULL;
 331        char one_value[SYSFS_PATH_MAX];
 332        char linebuf[MAX_LINE_LEN];
 333        unsigned int pos, i;
 334        unsigned int len;
 335
 336        len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies",
 337                                linebuf, sizeof(linebuf));
 338        if (len == 0)
 339                return NULL;
 340
 341        pos = 0;
 342        for (i = 0; i < len; i++) {
 343                if (linebuf[i] == ' ' || linebuf[i] == '\n') {
 344                        if (i - pos < 2)
 345                                continue;
 346                        if (i - pos >= SYSFS_PATH_MAX)
 347                                goto error_out;
 348                        if (current) {
 349                                current->next = malloc(sizeof(*current));
 350                                if (!current->next)
 351                                        goto error_out;
 352                                current = current->next;
 353                        } else {
 354                                first = malloc(sizeof(*first));
 355                                if (!first)
 356                                        goto error_out;
 357                                current = first;
 358                        }
 359                        current->first = first;
 360                        current->next = NULL;
 361
 362                        memcpy(one_value, linebuf + pos, i - pos);
 363                        one_value[i - pos] = '\0';
 364                        if (sscanf(one_value, "%lu", &current->frequency) != 1)
 365                                goto error_out;
 366
 367                        pos = i + 1;
 368                }
 369        }
 370
 371        return first;
 372
 373 error_out:
 374        while (first) {
 375                current = first->next;
 376                free(first);
 377                first = current;
 378        }
 379        return NULL;
 380}
 381
 382static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,
 383                                                        const char *file)
 384{
 385        struct cpufreq_affected_cpus *first = NULL;
 386        struct cpufreq_affected_cpus *current = NULL;
 387        char one_value[SYSFS_PATH_MAX];
 388        char linebuf[MAX_LINE_LEN];
 389        unsigned int pos, i;
 390        unsigned int len;
 391
 392        len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));
 393        if (len == 0)
 394                return NULL;
 395
 396        pos = 0;
 397        for (i = 0; i < len; i++) {
 398                if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {
 399                        if (i - pos  < 1)
 400                                continue;
 401                        if (i - pos >= SYSFS_PATH_MAX)
 402                                goto error_out;
 403                        if (current) {
 404                                current->next = malloc(sizeof(*current));
 405                                if (!current->next)
 406                                        goto error_out;
 407                                current = current->next;
 408                        } else {
 409                                first = malloc(sizeof(*first));
 410                                if (!first)
 411                                        goto error_out;
 412                                current = first;
 413                        }
 414                        current->first = first;
 415                        current->next = NULL;
 416
 417                        memcpy(one_value, linebuf + pos, i - pos);
 418                        one_value[i - pos] = '\0';
 419
 420                        if (sscanf(one_value, "%u", &current->cpu) != 1)
 421                                goto error_out;
 422
 423                        pos = i + 1;
 424                }
 425        }
 426
 427        return first;
 428
 429 error_out:
 430        while (first) {
 431                current = first->next;
 432                free(first);
 433                first = current;
 434        }
 435        return NULL;
 436}
 437
 438struct cpufreq_affected_cpus *sysfs_get_freq_affected_cpus(unsigned int cpu)
 439{
 440        return sysfs_get_cpu_list(cpu, "affected_cpus");
 441}
 442
 443struct cpufreq_affected_cpus *sysfs_get_freq_related_cpus(unsigned int cpu)
 444{
 445        return sysfs_get_cpu_list(cpu, "related_cpus");
 446}
 447
 448struct cpufreq_stats *sysfs_get_freq_stats(unsigned int cpu,
 449                                        unsigned long long *total_time) {
 450        struct cpufreq_stats *first = NULL;
 451        struct cpufreq_stats *current = NULL;
 452        char one_value[SYSFS_PATH_MAX];
 453        char linebuf[MAX_LINE_LEN];
 454        unsigned int pos, i;
 455        unsigned int len;
 456
 457        len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
 458                                linebuf, sizeof(linebuf));
 459        if (len == 0)
 460                return NULL;
 461
 462        *total_time = 0;
 463        pos = 0;
 464        for (i = 0; i < len; i++) {
 465                if (i == strlen(linebuf) || linebuf[i] == '\n') {
 466                        if (i - pos < 2)
 467                                continue;
 468                        if ((i - pos) >= SYSFS_PATH_MAX)
 469                                goto error_out;
 470                        if (current) {
 471                                current->next = malloc(sizeof(*current));
 472                                if (!current->next)
 473                                        goto error_out;
 474                                current = current->next;
 475                        } else {
 476                                first = malloc(sizeof(*first));
 477                                if (!first)
 478                                        goto error_out;
 479                                current = first;
 480                        }
 481                        current->first = first;
 482                        current->next = NULL;
 483
 484                        memcpy(one_value, linebuf + pos, i - pos);
 485                        one_value[i - pos] = '\0';
 486                        if (sscanf(one_value, "%lu %llu",
 487                                        &current->frequency,
 488                                        &current->time_in_state) != 2)
 489                                goto error_out;
 490
 491                        *total_time = *total_time + current->time_in_state;
 492                        pos = i + 1;
 493                }
 494        }
 495
 496        return first;
 497
 498 error_out:
 499        while (first) {
 500                current = first->next;
 501                free(first);
 502                first = current;
 503        }
 504        return NULL;
 505}
 506
 507unsigned long sysfs_get_freq_transitions(unsigned int cpu)
 508{
 509        return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
 510}
 511
 512static int verify_gov(char *new_gov, char *passed_gov)
 513{
 514        unsigned int i, j = 0;
 515
 516        if (!passed_gov || (strlen(passed_gov) > 19))
 517                return -EINVAL;
 518
 519        strncpy(new_gov, passed_gov, 20);
 520        for (i = 0; i < 20; i++) {
 521                if (j) {
 522                        new_gov[i] = '\0';
 523                        continue;
 524                }
 525                if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z'))
 526                        continue;
 527
 528                if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z'))
 529                        continue;
 530
 531                if (new_gov[i] == '-')
 532                        continue;
 533
 534                if (new_gov[i] == '_')
 535                        continue;
 536
 537                if (new_gov[i] == '\0') {
 538                        j = 1;
 539                        continue;
 540                }
 541                return -EINVAL;
 542        }
 543        new_gov[19] = '\0';
 544        return 0;
 545}
 546
 547int sysfs_modify_freq_policy_governor(unsigned int cpu, char *governor)
 548{
 549        char new_gov[SYSFS_PATH_MAX];
 550
 551        if (!governor)
 552                return -EINVAL;
 553
 554        if (verify_gov(new_gov, governor))
 555                return -EINVAL;
 556
 557        return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
 558                                             new_gov, strlen(new_gov));
 559};
 560
 561int sysfs_modify_freq_policy_max(unsigned int cpu, unsigned long max_freq)
 562{
 563        char value[SYSFS_PATH_MAX];
 564
 565        snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);
 566
 567        return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
 568                                             value, strlen(value));
 569};
 570
 571
 572int sysfs_modify_freq_policy_min(unsigned int cpu, unsigned long min_freq)
 573{
 574        char value[SYSFS_PATH_MAX];
 575
 576        snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);
 577
 578        return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,
 579                                             value, strlen(value));
 580};
 581
 582
 583int sysfs_set_freq_policy(unsigned int cpu, struct cpufreq_policy *policy)
 584{
 585        char min[SYSFS_PATH_MAX];
 586        char max[SYSFS_PATH_MAX];
 587        char gov[SYSFS_PATH_MAX];
 588        int ret;
 589        unsigned long old_min;
 590        int write_max_first;
 591
 592        if (!policy || !(policy->governor))
 593                return -EINVAL;
 594
 595        if (policy->max < policy->min)
 596                return -EINVAL;
 597
 598        if (verify_gov(gov, policy->governor))
 599                return -EINVAL;
 600
 601        snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);
 602        snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);
 603
 604        old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
 605        write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);
 606
 607        if (write_max_first) {
 608                ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
 609                                                    max, strlen(max));
 610                if (ret)
 611                        return ret;
 612        }
 613
 614        ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
 615                                            strlen(min));
 616        if (ret)
 617                return ret;
 618
 619        if (!write_max_first) {
 620                ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
 621                                                    max, strlen(max));
 622                if (ret)
 623                        return ret;
 624        }
 625
 626        return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
 627                                             gov, strlen(gov));
 628}
 629
 630int sysfs_set_frequency(unsigned int cpu, unsigned long target_frequency)
 631{
 632        struct cpufreq_policy *pol = sysfs_get_freq_policy(cpu);
 633        char userspace_gov[] = "userspace";
 634        char freq[SYSFS_PATH_MAX];
 635        int ret;
 636
 637        if (!pol)
 638                return -ENODEV;
 639
 640        if (strncmp(pol->governor, userspace_gov, 9) != 0) {
 641                ret = sysfs_modify_freq_policy_governor(cpu, userspace_gov);
 642                if (ret) {
 643                        cpufreq_put_policy(pol);
 644                        return ret;
 645                }
 646        }
 647
 648        cpufreq_put_policy(pol);
 649
 650        snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
 651
 652        return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
 653                                             freq, strlen(freq));
 654}
 655
 656/* CPUFREQ sysfs access **************************************************/
 657
 658/* General sysfs access **************************************************/
 659int sysfs_cpu_exists(unsigned int cpu)
 660{
 661        char file[SYSFS_PATH_MAX];
 662        struct stat statbuf;
 663
 664        snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/", cpu);
 665
 666        if (stat(file, &statbuf) != 0)
 667                return -ENOSYS;
 668
 669        return S_ISDIR(statbuf.st_mode) ? 0 : -ENOSYS;
 670}
 671
 672/* General sysfs access **************************************************/
 673