root/tools/power/cpupower/lib/cpuidle.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cpuidle_state_file_exists
  2. cpuidle_state_read_file
  3. cpuidle_state_write_file
  4. cpuidle_state_get_one_value
  5. cpuidle_state_get_one_string
  6. cpuidle_is_state_disabled
  7. cpuidle_state_disable
  8. cpuidle_state_latency
  9. cpuidle_state_usage
  10. cpuidle_state_time
  11. cpuidle_state_name
  12. cpuidle_state_desc
  13. cpuidle_state_count
  14. sysfs_cpuidle_read_file
  15. sysfs_cpuidle_get_one_string
  16. cpuidle_get_governor
  17. cpuidle_get_driver

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

/* [<][>][^][v][top][bottom][index][help] */