xref: /openbmc/linux/tools/power/cpupower/lib/powercap.c (revision 34d6f206a88c2651d216bd3487ac956a40b2ba8e)
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