test/devargs: fix memory leak
[dpdk.git] / lib / power / power_common.c
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 }