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