// SPDX-License-Identifier: GPL-2.0-only /* * (C) 2016 SUSE Software Solutions GmbH * Thomas Renninger <trenn@suse.de> */ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <stdio.h> #include <dirent.h> #include "powercap.h" static unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen) { int fd; ssize_t numread; fd = open(path, O_RDONLY); if (fd == -1) return 0; numread = read(fd, buf, buflen - 1); if (numread < 1) { close(fd); return 0; } buf[numread] = '\0'; close(fd); return (unsigned int) numread; } static int sysfs_get_enabled(char *path, int *mode) { int fd; char yes_no; int ret = 0; *mode = 0; fd = open(path, O_RDONLY); if (fd == -1) { ret = -1; goto out; } if (read(fd, &yes_no, 1) != 1) { ret = -1; goto out_close; } if (yes_no == '1') { *mode = 1; goto out_close; } else if (yes_no == '0') { goto out_close; } else { ret = -1; goto out_close; } out_close: close(fd); out: return ret; } int powercap_get_enabled(int *mode) { char path[SYSFS_PATH_MAX] = PATH_TO_POWERCAP "/intel-rapl/enabled"; return sysfs_get_enabled(path, mode); } /* * Hardcoded, because rapl is the only powercap implementation - * this needs to get more generic if more powercap implementations * should show up */ int powercap_get_driver(char *driver, int buflen) { char file[SYSFS_PATH_MAX] = PATH_TO_RAPL; struct stat statbuf; if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) { driver = ""; return -1; } else if (buflen > 10) { strcpy(driver, "intel-rapl"); return 0; } else return -1; } enum powercap_get64 { GET_ENERGY_UJ, GET_MAX_ENERGY_RANGE_UJ, GET_POWER_UW, GET_MAX_POWER_RANGE_UW, MAX_GET_64_FILES }; static const char *powercap_get64_files[MAX_GET_64_FILES] = { [GET_POWER_UW] = "power_uw", [GET_MAX_POWER_RANGE_UW] = "max_power_range_uw", [GET_ENERGY_UJ] = "energy_uj", [GET_MAX_ENERGY_RANGE_UJ] = "max_energy_range_uj", }; static int sysfs_powercap_get64_val(struct powercap_zone *zone, enum powercap_get64 which, uint64_t *val) { char file[SYSFS_PATH_MAX] = PATH_TO_POWERCAP "/"; int ret; char buf[MAX_LINE_LEN]; strcat(file, zone->sys_name); strcat(file, "/"); strcat(file, powercap_get64_files[which]); ret = sysfs_read_file(file, buf, MAX_LINE_LEN); if (ret < 0) return ret; if (ret == 0) return -1; *val = strtoll(buf, NULL, 10); return 0; } int powercap_get_max_energy_range_uj(struct powercap_zone *zone, uint64_t *val) { return sysfs_powercap_get64_val(zone, GET_MAX_ENERGY_RANGE_UJ, val); } int powercap_get_energy_uj(struct powercap_zone *zone, uint64_t *val) { return sysfs_powercap_get64_val(zone, GET_ENERGY_UJ, val); } int powercap_get_max_power_range_uw(struct powercap_zone *zone, uint64_t *val) { return sysfs_powercap_get64_val(zone, GET_MAX_POWER_RANGE_UW, val); } int powercap_get_power_uw(struct powercap_zone *zone, uint64_t *val) { return sysfs_powercap_get64_val(zone, GET_POWER_UW, val); } int powercap_zone_get_enabled(struct powercap_zone *zone, int *mode) { char path[SYSFS_PATH_MAX] = PATH_TO_POWERCAP; if ((strlen(PATH_TO_POWERCAP) + strlen(zone->sys_name)) + strlen("/enabled") + 1 >= SYSFS_PATH_MAX) return -1; strcat(path, "/"); strcat(path, zone->sys_name); strcat(path, "/enabled"); return sysfs_get_enabled(path, mode); } int powercap_zone_set_enabled(struct powercap_zone *zone, int mode) { /* To be done if needed */ return 0; } int powercap_read_zone(struct powercap_zone *zone) { struct dirent *dent; DIR *zone_dir; char sysfs_dir[SYSFS_PATH_MAX] = PATH_TO_POWERCAP; struct powercap_zone *child_zone; char file[SYSFS_PATH_MAX] = PATH_TO_POWERCAP; int i, ret = 0; uint64_t val = 0; strcat(sysfs_dir, "/"); strcat(sysfs_dir, zone->sys_name); zone_dir = opendir(sysfs_dir); if (zone_dir == NULL) return -1; strcat(file, "/"); strcat(file, zone->sys_name); strcat(file, "/name"); sysfs_read_file(file, zone->name, MAX_LINE_LEN); if (zone->parent) zone->tree_depth = zone->parent->tree_depth + 1; ret = powercap_get_energy_uj(zone, &val); if (ret == 0) zone->has_energy_uj = 1; ret = powercap_get_power_uw(zone, &val); if (ret == 0) zone->has_power_uw = 1; while ((dent = readdir(zone_dir)) != NULL) { struct stat st; if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue; if (stat(dent->d_name, &st) != 0 || !S_ISDIR(st.st_mode)) if (fstatat(dirfd(zone_dir), dent->d_name, &st, 0) < 0) continue; if (strncmp(dent->d_name, "intel-rapl:", 11) != 0) continue; child_zone = calloc(1, sizeof(struct powercap_zone)); if (child_zone == NULL) return -1; for (i = 0; i < POWERCAP_MAX_CHILD_ZONES; i++) { if (zone->children[i] == NULL) { zone->children[i] = child_zone; break; } if (i == POWERCAP_MAX_CHILD_ZONES - 1) { free(child_zone); fprintf(stderr, "Reached POWERCAP_MAX_CHILD_ZONES %d\n", POWERCAP_MAX_CHILD_ZONES); return -1; } } strcpy(child_zone->sys_name, zone->sys_name); strcat(child_zone->sys_name, "/"); strcat(child_zone->sys_name, dent->d_name); child_zone->parent = zone; if (zone->tree_depth >= POWERCAP_MAX_TREE_DEPTH) { fprintf(stderr, "Maximum zone hierarchy depth[%d] reached\n", POWERCAP_MAX_TREE_DEPTH); ret = -1; break; } powercap_read_zone(child_zone); } closedir(zone_dir); return ret; } struct powercap_zone *powercap_init_zones(void) { int enabled; struct powercap_zone *root_zone; int ret; char file[SYSFS_PATH_MAX] = PATH_TO_RAPL "/enabled"; ret = sysfs_get_enabled(file, &enabled); if (ret) return NULL; if (!enabled) return NULL; root_zone = calloc(1, sizeof(struct powercap_zone)); if (!root_zone) return NULL; strcpy(root_zone->sys_name, "intel-rapl/intel-rapl:0"); powercap_read_zone(root_zone); return root_zone; } /* Call function *f on the passed zone and all its children */ int powercap_walk_zones(struct powercap_zone *zone, int (*f)(struct powercap_zone *zone)) { int i, ret; if (!zone) return -1; ret = f(zone); if (ret) return ret; for (i = 0; i < POWERCAP_MAX_CHILD_ZONES; i++) { if (zone->children[i] != NULL) powercap_walk_zones(zone->children[i], f); } return 0; }