xref: /dpdk/lib/power/power_common.c (revision daa02b5cddbb8e11b31d41e2bf7bb1ae64dcae2f)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4 
5 #include <limits.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 
10 #include <rte_log.h>
11 #include <rte_string_fns.h>
12 
13 #include "power_common.h"
14 
15 #define POWER_SYSFILE_SCALING_DRIVER   \
16 		"/sys/devices/system/cpu/cpu%u/cpufreq/scaling_driver"
17 #define POWER_SYSFILE_GOVERNOR  \
18 		"/sys/devices/system/cpu/cpu%u/cpufreq/scaling_governor"
19 #define POWER_CONVERT_TO_DECIMAL 10
20 
21 int
22 cpufreq_check_scaling_driver(const char *driver_name)
23 {
24 	unsigned int lcore_id = 0; /* always check core 0 */
25 	char readbuf[PATH_MAX];
26 	size_t end_idx;
27 	char *s;
28 	FILE *f;
29 
30 	/*
31 	 * Check if scaling driver matches what we expect.
32 	 */
33 	open_core_sysfs_file(&f, "r", POWER_SYSFILE_SCALING_DRIVER,
34 			lcore_id);
35 	/* if there's no driver at all, bail out */
36 	if (f == NULL)
37 		return 0;
38 
39 	s = fgets(readbuf, sizeof(readbuf), f);
40 	/* don't need it any more */
41 	fclose(f);
42 
43 	/* if we can't read it, consider unsupported */
44 	if (s == NULL)
45 		return 0;
46 
47 	/* when read from sysfs, driver name has an extra newline at the end */
48 	end_idx = strnlen(readbuf, sizeof(readbuf));
49 	if (end_idx > 0 && readbuf[end_idx - 1] == '\n') {
50 		end_idx--;
51 		readbuf[end_idx] = '\0';
52 	}
53 
54 	/* does the driver name match? */
55 	if (strncmp(readbuf, driver_name, sizeof(readbuf)) != 0)
56 		return 0;
57 
58 	/*
59 	 * We might have a situation where the driver is supported, but we don't
60 	 * have permissions to do frequency scaling. This error should not be
61 	 * handled here, so consider the system to support scaling for now.
62 	 */
63 	return 1;
64 }
65 
66 int
67 open_core_sysfs_file(FILE **f, const char *mode, const char *format, ...)
68 {
69 	char fullpath[PATH_MAX];
70 	va_list ap;
71 	FILE *tmpf;
72 
73 	va_start(ap, format);
74 	vsnprintf(fullpath, sizeof(fullpath), format, ap);
75 	va_end(ap);
76 	tmpf = fopen(fullpath, mode);
77 	*f = tmpf;
78 	if (tmpf == NULL)
79 		return -1;
80 
81 	return 0;
82 }
83 
84 int
85 read_core_sysfs_u32(FILE *f, uint32_t *val)
86 {
87 	char buf[BUFSIZ];
88 	uint32_t fval;
89 	char *s;
90 
91 	s = fgets(buf, sizeof(buf), f);
92 	if (s == NULL)
93 		return -1;
94 
95 	/* fgets puts null terminator in, but do this just in case */
96 	buf[BUFSIZ - 1] = '\0';
97 
98 	/* strip off any terminating newlines */
99 	*strchrnul(buf, '\n') = '\0';
100 
101 	fval = strtoul(buf, NULL, POWER_CONVERT_TO_DECIMAL);
102 
103 	/* write the value */
104 	*val = fval;
105 
106 	return 0;
107 }
108 
109 int
110 read_core_sysfs_s(FILE *f, char *buf, unsigned int len)
111 {
112 	char *s;
113 
114 	s = fgets(buf, len, f);
115 	if (s == NULL)
116 		return -1;
117 
118 	/* fgets puts null terminator in, but do this just in case */
119 	buf[len - 1] = '\0';
120 
121 	/* strip off any terminating newlines */
122 	*strchrnul(buf, '\n') = '\0';
123 
124 	return 0;
125 }
126 
127 int
128 write_core_sysfs_s(FILE *f, const char *str)
129 {
130 	int ret;
131 
132 	ret = fseek(f, 0, SEEK_SET);
133 	if (ret != 0)
134 		return -1;
135 
136 	ret = fputs(str, f);
137 	if (ret < 0)
138 		return -1;
139 
140 	/* flush the output */
141 	ret = fflush(f);
142 	if (ret != 0)
143 		return -1;
144 
145 	return 0;
146 }
147 
148 /**
149  * It is to check the current scaling governor by reading sys file, and then
150  * set it into 'performance' if it is not by writing the sys file. The original
151  * governor will be saved for rolling back.
152  */
153 int
154 power_set_governor(unsigned int lcore_id, const char *new_governor,
155 		char *orig_governor, size_t orig_governor_len)
156 {
157 	FILE *f_governor = NULL;
158 	int ret = -1;
159 	char buf[BUFSIZ];
160 
161 	open_core_sysfs_file(&f_governor, "rw+", POWER_SYSFILE_GOVERNOR,
162 			lcore_id);
163 	if (f_governor == NULL) {
164 		RTE_LOG(ERR, POWER, "failed to open %s\n",
165 				POWER_SYSFILE_GOVERNOR);
166 		goto out;
167 	}
168 
169 	ret = read_core_sysfs_s(f_governor, buf, sizeof(buf));
170 	if (ret < 0) {
171 		RTE_LOG(ERR, POWER, "Failed to read %s\n",
172 				POWER_SYSFILE_GOVERNOR);
173 		goto out;
174 	}
175 
176 	/* Save the original governor, if it was provided */
177 	if (orig_governor)
178 		rte_strscpy(orig_governor, buf, orig_governor_len);
179 
180 	/* Check if current governor is already what we want */
181 	if (strcmp(buf, new_governor) == 0) {
182 		ret = 0;
183 		POWER_DEBUG_TRACE("Power management governor of lcore %u is "
184 				"already %s\n", lcore_id, new_governor);
185 		goto out;
186 	}
187 
188 	/* Write the new governor */
189 	ret = write_core_sysfs_s(f_governor, new_governor);
190 	if (ret < 0) {
191 		RTE_LOG(ERR, POWER, "Failed to write %s\n",
192 				POWER_SYSFILE_GOVERNOR);
193 		goto out;
194 	}
195 
196 	ret = 0;
197 	RTE_LOG(INFO, POWER, "Power management governor of lcore %u has been "
198 			"set to '%s' successfully\n", lcore_id, new_governor);
199 out:
200 	if (f_governor != NULL)
201 		fclose(f_governor);
202 
203 	return ret;
204 }
205