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