308 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * (C) 2016 SUSE Software Solutions GmbH4 * Thomas Renninger <trenn@suse.de>5 */6 7#include <sys/types.h>8#include <sys/stat.h>9#include <unistd.h>10#include <stdlib.h>11#include <string.h>12#include <fcntl.h>13#include <stdio.h>14#include <dirent.h>15 16#include "powercap.h"17 18static 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 39static int sysfs_get_enabled(char *path, int *mode)40{41 int fd;42 char yes_no;43 int ret = 0;44 45 *mode = 0;46 47 fd = open(path, O_RDONLY);48 if (fd == -1) {49 ret = -1;50 goto out;51 }52 53 if (read(fd, &yes_no, 1) != 1) {54 ret = -1;55 goto out_close;56 }57 58 if (yes_no == '1') {59 *mode = 1;60 goto out_close;61 } else if (yes_no == '0') {62 goto out_close;63 } else {64 ret = -1;65 goto out_close;66 }67out_close:68 close(fd);69out:70 return ret;71}72 73int powercap_get_enabled(int *mode)74{75 char path[SYSFS_PATH_MAX] = PATH_TO_POWERCAP "/intel-rapl/enabled";76 77 return sysfs_get_enabled(path, mode);78}79 80/*81 * TODO: implement function. Returns dummy 0 for now.82 */83int powercap_set_enabled(int mode)84{85 return 0;86}87 88/*89 * Hardcoded, because rapl is the only powercap implementation90- * this needs to get more generic if more powercap implementations91 * should show up92 */93int powercap_get_driver(char *driver, int buflen)94{95 char file[SYSFS_PATH_MAX] = PATH_TO_RAPL;96 97 struct stat statbuf;98 99 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) {100 driver = "";101 return -1;102 } else if (buflen > 10) {103 strcpy(driver, "intel-rapl");104 return 0;105 } else106 return -1;107}108 109enum powercap_get64 {110 GET_ENERGY_UJ,111 GET_MAX_ENERGY_RANGE_UJ,112 GET_POWER_UW,113 GET_MAX_POWER_RANGE_UW,114 MAX_GET_64_FILES115};116 117static const char *powercap_get64_files[MAX_GET_64_FILES] = {118 [GET_POWER_UW] = "power_uw",119 [GET_MAX_POWER_RANGE_UW] = "max_power_range_uw",120 [GET_ENERGY_UJ] = "energy_uj",121 [GET_MAX_ENERGY_RANGE_UJ] = "max_energy_range_uj",122};123 124static int sysfs_powercap_get64_val(struct powercap_zone *zone,125 enum powercap_get64 which,126 uint64_t *val)127{128 char file[SYSFS_PATH_MAX] = PATH_TO_POWERCAP "/";129 int ret;130 char buf[MAX_LINE_LEN];131 132 strcat(file, zone->sys_name);133 strcat(file, "/");134 strcat(file, powercap_get64_files[which]);135 136 ret = sysfs_read_file(file, buf, MAX_LINE_LEN);137 if (ret < 0)138 return ret;139 if (ret == 0)140 return -1;141 142 *val = strtoll(buf, NULL, 10);143 return 0;144}145 146int powercap_get_max_energy_range_uj(struct powercap_zone *zone, uint64_t *val)147{148 return sysfs_powercap_get64_val(zone, GET_MAX_ENERGY_RANGE_UJ, val);149}150 151int powercap_get_energy_uj(struct powercap_zone *zone, uint64_t *val)152{153 return sysfs_powercap_get64_val(zone, GET_ENERGY_UJ, val);154}155 156int powercap_get_max_power_range_uw(struct powercap_zone *zone, uint64_t *val)157{158 return sysfs_powercap_get64_val(zone, GET_MAX_POWER_RANGE_UW, val);159}160 161int powercap_get_power_uw(struct powercap_zone *zone, uint64_t *val)162{163 return sysfs_powercap_get64_val(zone, GET_POWER_UW, val);164}165 166int powercap_zone_get_enabled(struct powercap_zone *zone, int *mode)167{168 char path[SYSFS_PATH_MAX] = PATH_TO_POWERCAP;169 170 if ((strlen(PATH_TO_POWERCAP) + strlen(zone->sys_name)) +171 strlen("/enabled") + 1 >= SYSFS_PATH_MAX)172 return -1;173 174 strcat(path, "/");175 strcat(path, zone->sys_name);176 strcat(path, "/enabled");177 178 return sysfs_get_enabled(path, mode);179}180 181int powercap_zone_set_enabled(struct powercap_zone *zone, int mode)182{183 /* To be done if needed */184 return 0;185}186 187 188int powercap_read_zone(struct powercap_zone *zone)189{190 struct dirent *dent;191 DIR *zone_dir;192 char sysfs_dir[SYSFS_PATH_MAX] = PATH_TO_POWERCAP;193 struct powercap_zone *child_zone;194 char file[SYSFS_PATH_MAX] = PATH_TO_POWERCAP;195 int i, ret = 0;196 uint64_t val = 0;197 198 strcat(sysfs_dir, "/");199 strcat(sysfs_dir, zone->sys_name);200 201 zone_dir = opendir(sysfs_dir);202 if (zone_dir == NULL)203 return -1;204 205 strcat(file, "/");206 strcat(file, zone->sys_name);207 strcat(file, "/name");208 sysfs_read_file(file, zone->name, MAX_LINE_LEN);209 if (zone->parent)210 zone->tree_depth = zone->parent->tree_depth + 1;211 ret = powercap_get_energy_uj(zone, &val);212 if (ret == 0)213 zone->has_energy_uj = 1;214 ret = powercap_get_power_uw(zone, &val);215 if (ret == 0)216 zone->has_power_uw = 1;217 218 while ((dent = readdir(zone_dir)) != NULL) {219 struct stat st;220 221 if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)222 continue;223 224 if (stat(dent->d_name, &st) != 0 || !S_ISDIR(st.st_mode))225 if (fstatat(dirfd(zone_dir), dent->d_name, &st, 0) < 0)226 continue;227 228 if (strncmp(dent->d_name, "intel-rapl:", 11) != 0)229 continue;230 231 child_zone = calloc(1, sizeof(struct powercap_zone));232 if (child_zone == NULL)233 return -1;234 for (i = 0; i < POWERCAP_MAX_CHILD_ZONES; i++) {235 if (zone->children[i] == NULL) {236 zone->children[i] = child_zone;237 break;238 }239 if (i == POWERCAP_MAX_CHILD_ZONES - 1) {240 free(child_zone);241 fprintf(stderr, "Reached POWERCAP_MAX_CHILD_ZONES %d\n",242 POWERCAP_MAX_CHILD_ZONES);243 return -1;244 }245 }246 strcpy(child_zone->sys_name, zone->sys_name);247 strcat(child_zone->sys_name, "/");248 strcat(child_zone->sys_name, dent->d_name);249 child_zone->parent = zone;250 if (zone->tree_depth >= POWERCAP_MAX_TREE_DEPTH) {251 fprintf(stderr, "Maximum zone hierarchy depth[%d] reached\n",252 POWERCAP_MAX_TREE_DEPTH);253 ret = -1;254 break;255 }256 powercap_read_zone(child_zone);257 }258 closedir(zone_dir);259 return ret;260}261 262struct powercap_zone *powercap_init_zones(void)263{264 int enabled;265 struct powercap_zone *root_zone;266 int ret;267 char file[SYSFS_PATH_MAX] = PATH_TO_RAPL "/enabled";268 269 ret = sysfs_get_enabled(file, &enabled);270 271 if (ret)272 return NULL;273 274 if (!enabled)275 return NULL;276 277 root_zone = calloc(1, sizeof(struct powercap_zone));278 if (!root_zone)279 return NULL;280 281 strcpy(root_zone->sys_name, "intel-rapl/intel-rapl:0");282 283 powercap_read_zone(root_zone);284 285 return root_zone;286}287 288/* Call function *f on the passed zone and all its children */289 290int powercap_walk_zones(struct powercap_zone *zone,291 int (*f)(struct powercap_zone *zone))292{293 int i, ret;294 295 if (!zone)296 return -1;297 298 ret = f(zone);299 if (ret)300 return ret;301 302 for (i = 0; i < POWERCAP_MAX_CHILD_ZONES; i++) {303 if (zone->children[i] != NULL)304 powercap_walk_zones(zone->children[i], f);305 }306 return 0;307}308