linux/tools/power/cpupower/utils/helpers/sysfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
   4 *  (C) 2011       Thomas Renninger <trenn@novell.com> Novell Inc.
   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 "helpers/sysfs.h"
  17
  18unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
  19{
  20        int fd;
  21        ssize_t numread;
  22
  23        fd = open(path, O_RDONLY);
  24        if (fd == -1)
  25                return 0;
  26
  27        numread = read(fd, buf, buflen - 1);
  28        if (numread < 1) {
  29                close(fd);
  30                return 0;
  31        }
  32
  33        buf[numread] = '\0';
  34        close(fd);
  35
  36        return (unsigned int) numread;
  37}
  38
  39/*
  40 * Detect whether a CPU is online
  41 *
  42 * Returns:
  43 *     1 -> if CPU is online
  44 *     0 -> if CPU is offline
  45 *     negative errno values in error case
  46 */
  47int sysfs_is_cpu_online(unsigned int cpu)
  48{
  49        char path[SYSFS_PATH_MAX];
  50        int fd;
  51        ssize_t numread;
  52        unsigned long long value;
  53        char linebuf[MAX_LINE_LEN];
  54        char *endp;
  55        struct stat statbuf;
  56
  57        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
  58
  59        if (stat(path, &statbuf) != 0)
  60                return 0;
  61
  62        /*
  63         * kernel without CONFIG_HOTPLUG_CPU
  64         * -> cpuX directory exists, but not cpuX/online file
  65         */
  66        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
  67        if (stat(path, &statbuf) != 0)
  68                return 1;
  69
  70        fd = open(path, O_RDONLY);
  71        if (fd == -1)
  72                return -errno;
  73
  74        numread = read(fd, linebuf, MAX_LINE_LEN - 1);
  75        if (numread < 1) {
  76                close(fd);
  77                return -EIO;
  78        }
  79        linebuf[numread] = '\0';
  80        close(fd);
  81
  82        value = strtoull(linebuf, &endp, 0);
  83        if (value > 1)
  84                return -EINVAL;
  85
  86        return value;
  87}
  88
  89/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
  90
  91
  92/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
  93
  94/*
  95 * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir
  96 * exists.
  97 * For example the functionality to disable c-states was introduced in later
  98 * kernel versions, this function can be used to explicitly check for this
  99 * feature.
 100 *
 101 * returns 1 if the file exists, 0 otherwise.
 102 */
 103unsigned int sysfs_idlestate_file_exists(unsigned int cpu,
 104                                         unsigned int idlestate,
 105                                         const char *fname)
 106{
 107        char path[SYSFS_PATH_MAX];
 108        struct stat statbuf;
 109
 110
 111        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
 112                 cpu, idlestate, fname);
 113        if (stat(path, &statbuf) != 0)
 114                return 0;
 115        return 1;
 116}
 117
 118/*
 119 * helper function to read file from /sys into given buffer
 120 * fname is a relative path under "cpuX/cpuidle/stateX/" dir
 121 * cstates starting with 0, C0 is not counted as cstate.
 122 * This means if you want C1 info, pass 0 as idlestate param
 123 */
 124unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate,
 125                             const char *fname, char *buf, size_t buflen)
 126{
 127        char path[SYSFS_PATH_MAX];
 128        int fd;
 129        ssize_t numread;
 130
 131        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
 132                 cpu, idlestate, fname);
 133
 134        fd = open(path, O_RDONLY);
 135        if (fd == -1)
 136                return 0;
 137
 138        numread = read(fd, buf, buflen - 1);
 139        if (numread < 1) {
 140                close(fd);
 141                return 0;
 142        }
 143
 144        buf[numread] = '\0';
 145        close(fd);
 146
 147        return (unsigned int) numread;
 148}
 149
 150/* 
 151 * helper function to write a new value to a /sys file
 152 * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir
 153 *
 154 * Returns the number of bytes written or 0 on error
 155 */
 156static
 157unsigned int sysfs_idlestate_write_file(unsigned int cpu,
 158                                        unsigned int idlestate,
 159                                        const char *fname,
 160                                        const char *value, size_t len)
 161{
 162        char path[SYSFS_PATH_MAX];
 163        int fd;
 164        ssize_t numwrite;
 165
 166        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
 167                 cpu, idlestate, fname);
 168
 169        fd = open(path, O_WRONLY);
 170        if (fd == -1)
 171                return 0;
 172
 173        numwrite = write(fd, value, len);
 174        if (numwrite < 1) {
 175                close(fd);
 176                return 0;
 177        }
 178
 179        close(fd);
 180
 181        return (unsigned int) numwrite;
 182}
 183
 184/* read access to files which contain one numeric value */
 185
 186enum idlestate_value {
 187        IDLESTATE_USAGE,
 188        IDLESTATE_POWER,
 189        IDLESTATE_LATENCY,
 190        IDLESTATE_TIME,
 191        IDLESTATE_DISABLE,
 192        MAX_IDLESTATE_VALUE_FILES
 193};
 194
 195static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {
 196        [IDLESTATE_USAGE] = "usage",
 197        [IDLESTATE_POWER] = "power",
 198        [IDLESTATE_LATENCY] = "latency",
 199        [IDLESTATE_TIME]  = "time",
 200        [IDLESTATE_DISABLE]  = "disable",
 201};
 202
 203static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu,
 204                                                     unsigned int idlestate,
 205                                                     enum idlestate_value which)
 206{
 207        unsigned long long value;
 208        unsigned int len;
 209        char linebuf[MAX_LINE_LEN];
 210        char *endp;
 211
 212        if (which >= MAX_IDLESTATE_VALUE_FILES)
 213                return 0;
 214
 215        len = sysfs_idlestate_read_file(cpu, idlestate,
 216                                        idlestate_value_files[which],
 217                                        linebuf, sizeof(linebuf));
 218        if (len == 0)
 219                return 0;
 220
 221        value = strtoull(linebuf, &endp, 0);
 222
 223        if (endp == linebuf || errno == ERANGE)
 224                return 0;
 225
 226        return value;
 227}
 228
 229/* read access to files which contain one string */
 230
 231enum idlestate_string {
 232        IDLESTATE_DESC,
 233        IDLESTATE_NAME,
 234        MAX_IDLESTATE_STRING_FILES
 235};
 236
 237static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
 238        [IDLESTATE_DESC] = "desc",
 239        [IDLESTATE_NAME] = "name",
 240};
 241
 242
 243static char *sysfs_idlestate_get_one_string(unsigned int cpu,
 244                                        unsigned int idlestate,
 245                                        enum idlestate_string which)
 246{
 247        char linebuf[MAX_LINE_LEN];
 248        char *result;
 249        unsigned int len;
 250
 251        if (which >= MAX_IDLESTATE_STRING_FILES)
 252                return NULL;
 253
 254        len = sysfs_idlestate_read_file(cpu, idlestate,
 255                                        idlestate_string_files[which],
 256                                        linebuf, sizeof(linebuf));
 257        if (len == 0)
 258                return NULL;
 259
 260        result = strdup(linebuf);
 261        if (result == NULL)
 262                return NULL;
 263
 264        if (result[strlen(result) - 1] == '\n')
 265                result[strlen(result) - 1] = '\0';
 266
 267        return result;
 268}
 269
 270/*
 271 * Returns:
 272 *    1  if disabled
 273 *    0  if enabled
 274 *    -1 if idlestate is not available
 275 *    -2 if disabling is not supported by the kernel
 276 */
 277int sysfs_is_idlestate_disabled(unsigned int cpu,
 278                                unsigned int idlestate)
 279{
 280        if (sysfs_get_idlestate_count(cpu) <= idlestate)
 281                return -1;
 282
 283        if (!sysfs_idlestate_file_exists(cpu, idlestate,
 284                                 idlestate_value_files[IDLESTATE_DISABLE]))
 285                return -2;
 286        return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);
 287}
 288
 289/*
 290 * Pass 1 as last argument to disable or 0 to enable the state
 291 * Returns:
 292 *    0  on success
 293 *    negative values on error, for example:
 294 *      -1 if idlestate is not available
 295 *      -2 if disabling is not supported by the kernel
 296 *      -3 No write access to disable/enable C-states
 297 */
 298int sysfs_idlestate_disable(unsigned int cpu,
 299                            unsigned int idlestate,
 300                            unsigned int disable)
 301{
 302        char value[SYSFS_PATH_MAX];
 303        int bytes_written;
 304
 305        if (sysfs_get_idlestate_count(cpu) <= idlestate)
 306                return -1;
 307
 308        if (!sysfs_idlestate_file_exists(cpu, idlestate,
 309                                 idlestate_value_files[IDLESTATE_DISABLE]))
 310                return -2;
 311
 312        snprintf(value, SYSFS_PATH_MAX, "%u", disable);
 313
 314        bytes_written = sysfs_idlestate_write_file(cpu, idlestate, "disable",
 315                                                   value, sizeof(disable));
 316        if (bytes_written)
 317                return 0;
 318        return -3;
 319}
 320
 321unsigned long sysfs_get_idlestate_latency(unsigned int cpu,
 322                                          unsigned int idlestate)
 323{
 324        return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
 325}
 326
 327unsigned long sysfs_get_idlestate_usage(unsigned int cpu,
 328                                        unsigned int idlestate)
 329{
 330        return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
 331}
 332
 333unsigned long long sysfs_get_idlestate_time(unsigned int cpu,
 334                                        unsigned int idlestate)
 335{
 336        return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME);
 337}
 338
 339char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate)
 340{
 341        return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME);
 342}
 343
 344char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate)
 345{
 346        return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC);
 347}
 348
 349/*
 350 * Returns number of supported C-states of CPU core cpu
 351 * Negativ in error case
 352 * Zero if cpuidle does not export any C-states
 353 */
 354unsigned int sysfs_get_idlestate_count(unsigned int cpu)
 355{
 356        char file[SYSFS_PATH_MAX];
 357        struct stat statbuf;
 358        int idlestates = 1;
 359
 360
 361        snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
 362        if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
 363                return 0;
 364
 365        snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
 366        if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
 367                return 0;
 368
 369        while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
 370                snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
 371                         "cpu%u/cpuidle/state%d", cpu, idlestates);
 372                idlestates++;
 373        }
 374        idlestates--;
 375        return idlestates;
 376}
 377
 378/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
 379
 380/*
 381 * helper function to read file from /sys into given buffer
 382 * fname is a relative path under "cpu/cpuidle/" dir
 383 */
 384static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
 385                                            size_t buflen)
 386{
 387        char path[SYSFS_PATH_MAX];
 388
 389        snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
 390
 391        return sysfs_read_file(path, buf, buflen);
 392}
 393
 394
 395
 396/* read access to files which contain one string */
 397
 398enum cpuidle_string {
 399        CPUIDLE_GOVERNOR,
 400        CPUIDLE_GOVERNOR_RO,
 401        CPUIDLE_DRIVER,
 402        MAX_CPUIDLE_STRING_FILES
 403};
 404
 405static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
 406        [CPUIDLE_GOVERNOR]      = "current_governor",
 407        [CPUIDLE_GOVERNOR_RO]   = "current_governor_ro",
 408        [CPUIDLE_DRIVER]        = "current_driver",
 409};
 410
 411
 412static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
 413{
 414        char linebuf[MAX_LINE_LEN];
 415        char *result;
 416        unsigned int len;
 417
 418        if (which >= MAX_CPUIDLE_STRING_FILES)
 419                return NULL;
 420
 421        len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
 422                                linebuf, sizeof(linebuf));
 423        if (len == 0)
 424                return NULL;
 425
 426        result = strdup(linebuf);
 427        if (result == NULL)
 428                return NULL;
 429
 430        if (result[strlen(result) - 1] == '\n')
 431                result[strlen(result) - 1] = '\0';
 432
 433        return result;
 434}
 435
 436char *sysfs_get_cpuidle_governor(void)
 437{
 438        char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
 439        if (!tmp)
 440                return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
 441        else
 442                return tmp;
 443}
 444
 445char *sysfs_get_cpuidle_driver(void)
 446{
 447        return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
 448}
 449/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
 450
 451/*
 452 * Get sched_mc or sched_smt settings
 453 * Pass "mc" or "smt" as argument
 454 *
 455 * Returns negative value on failure
 456 */
 457int sysfs_get_sched(const char *smt_mc)
 458{
 459        return -ENODEV;
 460}
 461
 462/*
 463 * Get sched_mc or sched_smt settings
 464 * Pass "mc" or "smt" as argument
 465 *
 466 * Returns negative value on failure
 467 */
 468int sysfs_set_sched(const char *smt_mc, int val)
 469{
 470        return -ENODEV;
 471}
 472