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
cpufreq_check_scaling_driver(const char * driver_name)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
open_core_sysfs_file(FILE ** f,const char * mode,const char * format,...)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
read_core_sysfs_u32(FILE * f,uint32_t * val)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
read_core_sysfs_s(FILE * f,char * buf,unsigned int len)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
write_core_sysfs_s(FILE * f,const char * str)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
power_set_governor(unsigned int lcore_id,const char * new_governor,char * orig_governor,size_t orig_governor_len)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