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
sysfs_read_file(const char * path,char * buf,size_t buflen)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
sysfs_get_enabled(char * path,int * mode)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
powercap_get_enabled(int * mode)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 */
powercap_get_driver(char * driver,int buflen)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
sysfs_powercap_get64_val(struct powercap_zone * zone,enum powercap_get64 which,uint64_t * val)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
powercap_get_max_energy_range_uj(struct powercap_zone * zone,uint64_t * val)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
powercap_get_energy_uj(struct powercap_zone * zone,uint64_t * val)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
powercap_get_max_power_range_uw(struct powercap_zone * zone,uint64_t * val)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
powercap_get_power_uw(struct powercap_zone * zone,uint64_t * val)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
powercap_zone_get_enabled(struct powercap_zone * zone,int * mode)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
powercap_zone_set_enabled(struct powercap_zone * zone,int mode)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
powercap_read_zone(struct powercap_zone * zone)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
powercap_init_zones(void)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
powercap_walk_zones(struct powercap_zone * zone,int (* f)(struct powercap_zone * zone))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