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