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