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