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 "helpers/sysfs.h" 17 18 unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen) 19 { 20 int fd; 21 ssize_t numread; 22 23 fd = open(path, O_RDONLY); 24 if (fd == -1) 25 return 0; 26 27 numread = read(fd, buf, buflen - 1); 28 if (numread < 1) { 29 close(fd); 30 return 0; 31 } 32 33 buf[numread] = '\0'; 34 close(fd); 35 36 return (unsigned int) numread; 37 } 38 39 /* 40 * Detect whether a CPU is online 41 * 42 * Returns: 43 * 1 -> if CPU is online 44 * 0 -> if CPU is offline 45 * negative errno values in error case 46 */ 47 int sysfs_is_cpu_online(unsigned int cpu) 48 { 49 char path[SYSFS_PATH_MAX]; 50 int fd; 51 ssize_t numread; 52 unsigned long long value; 53 char linebuf[MAX_LINE_LEN]; 54 char *endp; 55 struct stat statbuf; 56 57 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu); 58 59 if (stat(path, &statbuf) != 0) 60 return 0; 61 62 /* 63 * kernel without CONFIG_HOTPLUG_CPU 64 * -> cpuX directory exists, but not cpuX/online file 65 */ 66 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu); 67 if (stat(path, &statbuf) != 0) 68 return 1; 69 70 fd = open(path, O_RDONLY); 71 if (fd == -1) 72 return -errno; 73 74 numread = read(fd, linebuf, MAX_LINE_LEN - 1); 75 if (numread < 1) { 76 close(fd); 77 return -EIO; 78 } 79 linebuf[numread] = '\0'; 80 close(fd); 81 82 value = strtoull(linebuf, &endp, 0); 83 if (value > 1) 84 return -EINVAL; 85 86 return value; 87 } 88 89 /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ 90 91 92 /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ 93 94 /* 95 * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir 96 * exists. 97 * For example the functionality to disable c-states was introduced in later 98 * kernel versions, this function can be used to explicitly check for this 99 * feature. 100 * 101 * returns 1 if the file exists, 0 otherwise. 102 */ 103 unsigned int sysfs_idlestate_file_exists(unsigned int cpu, 104 unsigned int idlestate, 105 const char *fname) 106 { 107 char path[SYSFS_PATH_MAX]; 108 struct stat statbuf; 109 110 111 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", 112 cpu, idlestate, fname); 113 if (stat(path, &statbuf) != 0) 114 return 0; 115 return 1; 116 } 117 118 /* 119 * helper function to read file from /sys into given buffer 120 * fname is a relative path under "cpuX/cpuidle/stateX/" dir 121 * cstates starting with 0, C0 is not counted as cstate. 122 * This means if you want C1 info, pass 0 as idlestate param 123 */ 124 unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate, 125 const char *fname, char *buf, size_t buflen) 126 { 127 char path[SYSFS_PATH_MAX]; 128 int fd; 129 ssize_t numread; 130 131 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", 132 cpu, idlestate, fname); 133 134 fd = open(path, O_RDONLY); 135 if (fd == -1) 136 return 0; 137 138 numread = read(fd, buf, buflen - 1); 139 if (numread < 1) { 140 close(fd); 141 return 0; 142 } 143 144 buf[numread] = '\0'; 145 close(fd); 146 147 return (unsigned int) numread; 148 } 149 150 /* 151 * helper function to write a new value to a /sys file 152 * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir 153 * 154 * Returns the number of bytes written or 0 on error 155 */ 156 static 157 unsigned int sysfs_idlestate_write_file(unsigned int cpu, 158 unsigned int idlestate, 159 const char *fname, 160 const char *value, size_t len) 161 { 162 char path[SYSFS_PATH_MAX]; 163 int fd; 164 ssize_t numwrite; 165 166 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", 167 cpu, idlestate, fname); 168 169 fd = open(path, O_WRONLY); 170 if (fd == -1) 171 return 0; 172 173 numwrite = write(fd, value, len); 174 if (numwrite < 1) { 175 close(fd); 176 return 0; 177 } 178 179 close(fd); 180 181 return (unsigned int) numwrite; 182 } 183 184 /* read access to files which contain one numeric value */ 185 186 enum idlestate_value { 187 IDLESTATE_USAGE, 188 IDLESTATE_POWER, 189 IDLESTATE_LATENCY, 190 IDLESTATE_TIME, 191 IDLESTATE_DISABLE, 192 MAX_IDLESTATE_VALUE_FILES 193 }; 194 195 static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = { 196 [IDLESTATE_USAGE] = "usage", 197 [IDLESTATE_POWER] = "power", 198 [IDLESTATE_LATENCY] = "latency", 199 [IDLESTATE_TIME] = "time", 200 [IDLESTATE_DISABLE] = "disable", 201 }; 202 203 static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu, 204 unsigned int idlestate, 205 enum idlestate_value which) 206 { 207 unsigned long long value; 208 unsigned int len; 209 char linebuf[MAX_LINE_LEN]; 210 char *endp; 211 212 if (which >= MAX_IDLESTATE_VALUE_FILES) 213 return 0; 214 215 len = sysfs_idlestate_read_file(cpu, idlestate, 216 idlestate_value_files[which], 217 linebuf, sizeof(linebuf)); 218 if (len == 0) 219 return 0; 220 221 value = strtoull(linebuf, &endp, 0); 222 223 if (endp == linebuf || errno == ERANGE) 224 return 0; 225 226 return value; 227 } 228 229 /* read access to files which contain one string */ 230 231 enum idlestate_string { 232 IDLESTATE_DESC, 233 IDLESTATE_NAME, 234 MAX_IDLESTATE_STRING_FILES 235 }; 236 237 static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = { 238 [IDLESTATE_DESC] = "desc", 239 [IDLESTATE_NAME] = "name", 240 }; 241 242 243 static char *sysfs_idlestate_get_one_string(unsigned int cpu, 244 unsigned int idlestate, 245 enum idlestate_string which) 246 { 247 char linebuf[MAX_LINE_LEN]; 248 char *result; 249 unsigned int len; 250 251 if (which >= MAX_IDLESTATE_STRING_FILES) 252 return NULL; 253 254 len = sysfs_idlestate_read_file(cpu, idlestate, 255 idlestate_string_files[which], 256 linebuf, sizeof(linebuf)); 257 if (len == 0) 258 return NULL; 259 260 result = strdup(linebuf); 261 if (result == NULL) 262 return NULL; 263 264 if (result[strlen(result) - 1] == '\n') 265 result[strlen(result) - 1] = '\0'; 266 267 return result; 268 } 269 270 /* 271 * Returns: 272 * 1 if disabled 273 * 0 if enabled 274 * -1 if idlestate is not available 275 * -2 if disabling is not supported by the kernel 276 */ 277 int sysfs_is_idlestate_disabled(unsigned int cpu, 278 unsigned int idlestate) 279 { 280 if (sysfs_get_idlestate_count(cpu) <= idlestate) 281 return -1; 282 283 if (!sysfs_idlestate_file_exists(cpu, idlestate, 284 idlestate_value_files[IDLESTATE_DISABLE])) 285 return -2; 286 return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_DISABLE); 287 } 288 289 /* 290 * Pass 1 as last argument to disable or 0 to enable the state 291 * Returns: 292 * 0 on success 293 * negative values on error, for example: 294 * -1 if idlestate is not available 295 * -2 if disabling is not supported by the kernel 296 * -3 No write access to disable/enable C-states 297 */ 298 int sysfs_idlestate_disable(unsigned int cpu, 299 unsigned int idlestate, 300 unsigned int disable) 301 { 302 char value[SYSFS_PATH_MAX]; 303 int bytes_written; 304 305 if (sysfs_get_idlestate_count(cpu) <= idlestate) 306 return -1; 307 308 if (!sysfs_idlestate_file_exists(cpu, idlestate, 309 idlestate_value_files[IDLESTATE_DISABLE])) 310 return -2; 311 312 snprintf(value, SYSFS_PATH_MAX, "%u", disable); 313 314 bytes_written = sysfs_idlestate_write_file(cpu, idlestate, "disable", 315 value, sizeof(disable)); 316 if (bytes_written) 317 return 0; 318 return -3; 319 } 320 321 unsigned long sysfs_get_idlestate_latency(unsigned int cpu, 322 unsigned int idlestate) 323 { 324 return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); 325 } 326 327 unsigned long sysfs_get_idlestate_usage(unsigned int cpu, 328 unsigned int idlestate) 329 { 330 return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE); 331 } 332 333 unsigned long long sysfs_get_idlestate_time(unsigned int cpu, 334 unsigned int idlestate) 335 { 336 return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME); 337 } 338 339 char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate) 340 { 341 return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME); 342 } 343 344 char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate) 345 { 346 return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC); 347 } 348 349 /* 350 * Returns number of supported C-states of CPU core cpu 351 * Negativ in error case 352 * Zero if cpuidle does not export any C-states 353 */ 354 unsigned int sysfs_get_idlestate_count(unsigned int cpu) 355 { 356 char file[SYSFS_PATH_MAX]; 357 struct stat statbuf; 358 int idlestates = 1; 359 360 361 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle"); 362 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) 363 return 0; 364 365 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu); 366 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) 367 return 0; 368 369 while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { 370 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU 371 "cpu%u/cpuidle/state%d", cpu, idlestates); 372 idlestates++; 373 } 374 idlestates--; 375 return idlestates; 376 } 377 378 /* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/ 379 380 /* 381 * helper function to read file from /sys into given buffer 382 * fname is a relative path under "cpu/cpuidle/" dir 383 */ 384 static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf, 385 size_t buflen) 386 { 387 char path[SYSFS_PATH_MAX]; 388 389 snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname); 390 391 return sysfs_read_file(path, buf, buflen); 392 } 393 394 395 396 /* read access to files which contain one string */ 397 398 enum cpuidle_string { 399 CPUIDLE_GOVERNOR, 400 CPUIDLE_GOVERNOR_RO, 401 CPUIDLE_DRIVER, 402 MAX_CPUIDLE_STRING_FILES 403 }; 404 405 static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = { 406 [CPUIDLE_GOVERNOR] = "current_governor", 407 [CPUIDLE_GOVERNOR_RO] = "current_governor_ro", 408 [CPUIDLE_DRIVER] = "current_driver", 409 }; 410 411 412 static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which) 413 { 414 char linebuf[MAX_LINE_LEN]; 415 char *result; 416 unsigned int len; 417 418 if (which >= MAX_CPUIDLE_STRING_FILES) 419 return NULL; 420 421 len = sysfs_cpuidle_read_file(cpuidle_string_files[which], 422 linebuf, sizeof(linebuf)); 423 if (len == 0) 424 return NULL; 425 426 result = strdup(linebuf); 427 if (result == NULL) 428 return NULL; 429 430 if (result[strlen(result) - 1] == '\n') 431 result[strlen(result) - 1] = '\0'; 432 433 return result; 434 } 435 436 char *sysfs_get_cpuidle_governor(void) 437 { 438 char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO); 439 if (!tmp) 440 return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR); 441 else 442 return tmp; 443 } 444 445 char *sysfs_get_cpuidle_driver(void) 446 { 447 return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER); 448 } 449 /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ 450 451 /* 452 * Get sched_mc or sched_smt settings 453 * Pass "mc" or "smt" as argument 454 * 455 * Returns negative value on failure 456 */ 457 int sysfs_get_sched(const char *smt_mc) 458 { 459 return -ENODEV; 460 } 461 462 /* 463 * Get sched_mc or sched_smt settings 464 * Pass "mc" or "smt" as argument 465 * 466 * Returns negative value on failure 467 */ 468 int sysfs_set_sched(const char *smt_mc, int val) 469 { 470 return -ENODEV; 471 } 472