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