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