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