linux/tools/power/cpupower/lib/cpuidle.c
<<
>>
Prefs
   1/*
   2 *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
   3 *  (C) 2011       Thomas Renninger <trenn@novell.com> Novell Inc.
   4 *
   5 *  Licensed under the terms of the GNU GPL License version 2.
   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 "cpuidle.h"
  18#include "cpupower_intern.h"
  19
  20/*
  21 * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir
  22 * exists.
  23 * For example the functionality to disable c-states was introduced in later
  24 * kernel versions, this function can be used to explicitly check for this
  25 * feature.
  26 *
  27 * returns 1 if the file exists, 0 otherwise.
  28 */
  29static
  30unsigned int cpuidle_state_file_exists(unsigned int cpu,
  31                                       unsigned int idlestate,
  32                                       const char *fname)
  33{
  34        char path[SYSFS_PATH_MAX];
  35        struct stat statbuf;
  36
  37
  38        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
  39                 cpu, idlestate, fname);
  40        if (stat(path, &statbuf) != 0)
  41                return 0;
  42        return 1;
  43}
  44
  45/*
  46 * helper function to read file from /sys into given buffer
  47 * fname is a relative path under "cpuX/cpuidle/stateX/" dir
  48 * cstates starting with 0, C0 is not counted as cstate.
  49 * This means if you want C1 info, pass 0 as idlestate param
  50 */
  51static
  52unsigned int cpuidle_state_read_file(unsigned int cpu,
  53                                            unsigned int idlestate,
  54                                            const char *fname, char *buf,
  55                                            size_t buflen)
  56{
  57        char path[SYSFS_PATH_MAX];
  58        int fd;
  59        ssize_t numread;
  60
  61        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
  62                 cpu, idlestate, fname);
  63
  64        fd = open(path, O_RDONLY);
  65        if (fd == -1)
  66                return 0;
  67
  68        numread = read(fd, buf, buflen - 1);
  69        if (numread < 1) {
  70                close(fd);
  71                return 0;
  72        }
  73
  74        buf[numread] = '\0';
  75        close(fd);
  76
  77        return (unsigned int) numread;
  78}
  79
  80/*
  81 * helper function to write a new value to a /sys file
  82 * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir
  83 *
  84 * Returns the number of bytes written or 0 on error
  85 */
  86static
  87unsigned int cpuidle_state_write_file(unsigned int cpu,
  88                                      unsigned int idlestate,
  89                                      const char *fname,
  90                                      const char *value, size_t len)
  91{
  92        char path[SYSFS_PATH_MAX];
  93        int fd;
  94        ssize_t numwrite;
  95
  96        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
  97                 cpu, idlestate, fname);
  98
  99        fd = open(path, O_WRONLY);
 100        if (fd == -1)
 101                return 0;
 102
 103        numwrite = write(fd, value, len);
 104        if (numwrite < 1) {
 105                close(fd);
 106                return 0;
 107        }
 108
 109        close(fd);
 110
 111        return (unsigned int) numwrite;
 112}
 113
 114/* read access to files which contain one numeric value */
 115
 116enum idlestate_value {
 117        IDLESTATE_USAGE,
 118        IDLESTATE_POWER,
 119        IDLESTATE_LATENCY,
 120        IDLESTATE_TIME,
 121        IDLESTATE_DISABLE,
 122        MAX_IDLESTATE_VALUE_FILES
 123};
 124
 125static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {
 126        [IDLESTATE_USAGE] = "usage",
 127        [IDLESTATE_POWER] = "power",
 128        [IDLESTATE_LATENCY] = "latency",
 129        [IDLESTATE_TIME]  = "time",
 130        [IDLESTATE_DISABLE]  = "disable",
 131};
 132
 133static
 134unsigned long long cpuidle_state_get_one_value(unsigned int cpu,
 135                                               unsigned int idlestate,
 136                                               enum idlestate_value which)
 137{
 138        unsigned long long value;
 139        unsigned int len;
 140        char linebuf[MAX_LINE_LEN];
 141        char *endp;
 142
 143        if (which >= MAX_IDLESTATE_VALUE_FILES)
 144                return 0;
 145
 146        len = cpuidle_state_read_file(cpu, idlestate,
 147                                      idlestate_value_files[which],
 148                                      linebuf, sizeof(linebuf));
 149        if (len == 0)
 150                return 0;
 151
 152        value = strtoull(linebuf, &endp, 0);
 153
 154        if (endp == linebuf || errno == ERANGE)
 155                return 0;
 156
 157        return value;
 158}
 159
 160/* read access to files which contain one string */
 161
 162enum idlestate_string {
 163        IDLESTATE_DESC,
 164        IDLESTATE_NAME,
 165        MAX_IDLESTATE_STRING_FILES
 166};
 167
 168static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
 169        [IDLESTATE_DESC] = "desc",
 170        [IDLESTATE_NAME] = "name",
 171};
 172
 173
 174static char *cpuidle_state_get_one_string(unsigned int cpu,
 175                                        unsigned int idlestate,
 176                                        enum idlestate_string which)
 177{
 178        char linebuf[MAX_LINE_LEN];
 179        char *result;
 180        unsigned int len;
 181
 182        if (which >= MAX_IDLESTATE_STRING_FILES)
 183                return NULL;
 184
 185        len = cpuidle_state_read_file(cpu, idlestate,
 186                                      idlestate_string_files[which],
 187                                      linebuf, sizeof(linebuf));
 188        if (len == 0)
 189                return NULL;
 190
 191        result = strdup(linebuf);
 192        if (result == NULL)
 193                return NULL;
 194
 195        if (result[strlen(result) - 1] == '\n')
 196                result[strlen(result) - 1] = '\0';
 197
 198        return result;
 199}
 200
 201/*
 202 * Returns:
 203 *    1  if disabled
 204 *    0  if enabled
 205 *    -1 if idlestate is not available
 206 *    -2 if disabling is not supported by the kernel
 207 */
 208int cpuidle_is_state_disabled(unsigned int cpu,
 209                                unsigned int idlestate)
 210{
 211        if (cpuidle_state_count(cpu) <= idlestate)
 212                return -1;
 213
 214        if (!cpuidle_state_file_exists(cpu, idlestate,
 215                                 idlestate_value_files[IDLESTATE_DISABLE]))
 216                return -2;
 217        return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);
 218}
 219
 220/*
 221 * Pass 1 as last argument to disable or 0 to enable the state
 222 * Returns:
 223 *    0  on success
 224 *    negative values on error, for example:
 225 *      -1 if idlestate is not available
 226 *      -2 if disabling is not supported by the kernel
 227 *      -3 No write access to disable/enable C-states
 228 */
 229int cpuidle_state_disable(unsigned int cpu,
 230                            unsigned int idlestate,
 231                            unsigned int disable)
 232{
 233        char value[SYSFS_PATH_MAX];
 234        int bytes_written;
 235
 236        if (cpuidle_state_count(cpu) <= idlestate)
 237                return -1;
 238
 239        if (!cpuidle_state_file_exists(cpu, idlestate,
 240                                 idlestate_value_files[IDLESTATE_DISABLE]))
 241                return -2;
 242
 243        snprintf(value, SYSFS_PATH_MAX, "%u", disable);
 244
 245        bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable",
 246                                                   value, sizeof(disable));
 247        if (bytes_written)
 248                return 0;
 249        return -3;
 250}
 251
 252unsigned long cpuidle_state_latency(unsigned int cpu,
 253                                          unsigned int idlestate)
 254{
 255        return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
 256}
 257
 258unsigned long cpuidle_state_usage(unsigned int cpu,
 259                                        unsigned int idlestate)
 260{
 261        return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
 262}
 263
 264unsigned long long cpuidle_state_time(unsigned int cpu,
 265                                        unsigned int idlestate)
 266{
 267        return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME);
 268}
 269
 270char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate)
 271{
 272        return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME);
 273}
 274
 275char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate)
 276{
 277        return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC);
 278}
 279
 280/*
 281 * Returns number of supported C-states of CPU core cpu
 282 * Negativ in error case
 283 * Zero if cpuidle does not export any C-states
 284 */
 285unsigned int cpuidle_state_count(unsigned int cpu)
 286{
 287        char file[SYSFS_PATH_MAX];
 288        struct stat statbuf;
 289        int idlestates = 1;
 290
 291
 292        snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
 293        if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
 294                return 0;
 295
 296        snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
 297        if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
 298                return 0;
 299
 300        while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
 301                snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
 302                         "cpu%u/cpuidle/state%d", cpu, idlestates);
 303                idlestates++;
 304        }
 305        idlestates--;
 306        return idlestates;
 307}
 308
 309/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
 310
 311/*
 312 * helper function to read file from /sys into given buffer
 313 * fname is a relative path under "cpu/cpuidle/" dir
 314 */
 315static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
 316                                            size_t buflen)
 317{
 318        char path[SYSFS_PATH_MAX];
 319
 320        snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
 321
 322        return sysfs_read_file(path, buf, buflen);
 323}
 324
 325
 326
 327/* read access to files which contain one string */
 328
 329enum cpuidle_string {
 330        CPUIDLE_GOVERNOR,
 331        CPUIDLE_GOVERNOR_RO,
 332        CPUIDLE_DRIVER,
 333        MAX_CPUIDLE_STRING_FILES
 334};
 335
 336static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
 337        [CPUIDLE_GOVERNOR]      = "current_governor",
 338        [CPUIDLE_GOVERNOR_RO]   = "current_governor_ro",
 339        [CPUIDLE_DRIVER]        = "current_driver",
 340};
 341
 342
 343static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
 344{
 345        char linebuf[MAX_LINE_LEN];
 346        char *result;
 347        unsigned int len;
 348
 349        if (which >= MAX_CPUIDLE_STRING_FILES)
 350                return NULL;
 351
 352        len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
 353                                linebuf, sizeof(linebuf));
 354        if (len == 0)
 355                return NULL;
 356
 357        result = strdup(linebuf);
 358        if (result == NULL)
 359                return NULL;
 360
 361        if (result[strlen(result) - 1] == '\n')
 362                result[strlen(result) - 1] = '\0';
 363
 364        return result;
 365}
 366
 367char *cpuidle_get_governor(void)
 368{
 369        char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
 370        if (!tmp)
 371                return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
 372        else
 373                return tmp;
 374}
 375
 376char *cpuidle_get_driver(void)
 377{
 378        return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
 379}
 380/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
 381