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 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 } 67 out_close: 68 close(fd); 69 out: 70 return ret; 71 } 72 73 int 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 */ 83 int powercap_set_enabled(int mode) 84 { 85 return 0; 86 } 87 88 /* 89 * Hardcoded, because rapl is the only powercap implementation 90 - * this needs to get more generic if more powercap implementations 91 * should show up 92 */ 93 int 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 } else 106 return -1; 107 } 108 109 enum 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_FILES 115 }; 116 117 static 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 124 static 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 146 int 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 151 int 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 156 int 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 161 int 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 166 int 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 181 int powercap_zone_set_enabled(struct powercap_zone *zone, int mode) 182 { 183 /* To be done if needed */ 184 return 0; 185 } 186 187 188 int 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 262 struct 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 290 int 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