1
2
3
4
5
6
7
8#include <stdio.h>
9#include <errno.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/types.h>
13#include <sys/stat.h>
14#include <fcntl.h>
15#include <unistd.h>
16
17#include "cpuidle.h"
18#include "cpupower_intern.h"
19
20
21
22
23
24
25
26
27
28
29static
30unsigned int cpuidle_state_file_exists(unsigned int cpu,
31 unsigned int idlestate,
32 const char *fname)
33{
34 char path[SYSFS_PATH_MAX];
35 struct stat statbuf;
36
37
38 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
39 cpu, idlestate, fname);
40 if (stat(path, &statbuf) != 0)
41 return 0;
42 return 1;
43}
44
45
46
47
48
49
50
51static
52unsigned int cpuidle_state_read_file(unsigned int cpu,
53 unsigned int idlestate,
54 const char *fname, char *buf,
55 size_t buflen)
56{
57 char path[SYSFS_PATH_MAX];
58 int fd;
59 ssize_t numread;
60
61 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
62 cpu, idlestate, fname);
63
64 fd = open(path, O_RDONLY);
65 if (fd == -1)
66 return 0;
67
68 numread = read(fd, buf, buflen - 1);
69 if (numread < 1) {
70 close(fd);
71 return 0;
72 }
73
74 buf[numread] = '\0';
75 close(fd);
76
77 return (unsigned int) numread;
78}
79
80
81
82
83
84
85
86static
87unsigned int cpuidle_state_write_file(unsigned int cpu,
88 unsigned int idlestate,
89 const char *fname,
90 const char *value, size_t len)
91{
92 char path[SYSFS_PATH_MAX];
93 int fd;
94 ssize_t numwrite;
95
96 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
97 cpu, idlestate, fname);
98
99 fd = open(path, O_WRONLY);
100 if (fd == -1)
101 return 0;
102
103 numwrite = write(fd, value, len);
104 if (numwrite < 1) {
105 close(fd);
106 return 0;
107 }
108
109 close(fd);
110
111 return (unsigned int) numwrite;
112}
113
114
115
116enum idlestate_value {
117 IDLESTATE_USAGE,
118 IDLESTATE_POWER,
119 IDLESTATE_LATENCY,
120 IDLESTATE_TIME,
121 IDLESTATE_DISABLE,
122 MAX_IDLESTATE_VALUE_FILES
123};
124
125static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {
126 [IDLESTATE_USAGE] = "usage",
127 [IDLESTATE_POWER] = "power",
128 [IDLESTATE_LATENCY] = "latency",
129 [IDLESTATE_TIME] = "time",
130 [IDLESTATE_DISABLE] = "disable",
131};
132
133static
134unsigned long long cpuidle_state_get_one_value(unsigned int cpu,
135 unsigned int idlestate,
136 enum idlestate_value which)
137{
138 unsigned long long value;
139 unsigned int len;
140 char linebuf[MAX_LINE_LEN];
141 char *endp;
142
143 if (which >= MAX_IDLESTATE_VALUE_FILES)
144 return 0;
145
146 len = cpuidle_state_read_file(cpu, idlestate,
147 idlestate_value_files[which],
148 linebuf, sizeof(linebuf));
149 if (len == 0)
150 return 0;
151
152 value = strtoull(linebuf, &endp, 0);
153
154 if (endp == linebuf || errno == ERANGE)
155 return 0;
156
157 return value;
158}
159
160
161
162enum idlestate_string {
163 IDLESTATE_DESC,
164 IDLESTATE_NAME,
165 MAX_IDLESTATE_STRING_FILES
166};
167
168static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
169 [IDLESTATE_DESC] = "desc",
170 [IDLESTATE_NAME] = "name",
171};
172
173
174static char *cpuidle_state_get_one_string(unsigned int cpu,
175 unsigned int idlestate,
176 enum idlestate_string which)
177{
178 char linebuf[MAX_LINE_LEN];
179 char *result;
180 unsigned int len;
181
182 if (which >= MAX_IDLESTATE_STRING_FILES)
183 return NULL;
184
185 len = cpuidle_state_read_file(cpu, idlestate,
186 idlestate_string_files[which],
187 linebuf, sizeof(linebuf));
188 if (len == 0)
189 return NULL;
190
191 result = strdup(linebuf);
192 if (result == NULL)
193 return NULL;
194
195 if (result[strlen(result) - 1] == '\n')
196 result[strlen(result) - 1] = '\0';
197
198 return result;
199}
200
201
202
203
204
205
206
207
208int cpuidle_is_state_disabled(unsigned int cpu,
209 unsigned int idlestate)
210{
211 if (cpuidle_state_count(cpu) <= idlestate)
212 return -1;
213
214 if (!cpuidle_state_file_exists(cpu, idlestate,
215 idlestate_value_files[IDLESTATE_DISABLE]))
216 return -2;
217 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);
218}
219
220
221
222
223
224
225
226
227
228
229int cpuidle_state_disable(unsigned int cpu,
230 unsigned int idlestate,
231 unsigned int disable)
232{
233 char value[SYSFS_PATH_MAX];
234 int bytes_written;
235
236 if (cpuidle_state_count(cpu) <= idlestate)
237 return -1;
238
239 if (!cpuidle_state_file_exists(cpu, idlestate,
240 idlestate_value_files[IDLESTATE_DISABLE]))
241 return -2;
242
243 snprintf(value, SYSFS_PATH_MAX, "%u", disable);
244
245 bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable",
246 value, sizeof(disable));
247 if (bytes_written)
248 return 0;
249 return -3;
250}
251
252unsigned long cpuidle_state_latency(unsigned int cpu,
253 unsigned int idlestate)
254{
255 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
256}
257
258unsigned long cpuidle_state_usage(unsigned int cpu,
259 unsigned int idlestate)
260{
261 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
262}
263
264unsigned long long cpuidle_state_time(unsigned int cpu,
265 unsigned int idlestate)
266{
267 return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME);
268}
269
270char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate)
271{
272 return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME);
273}
274
275char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate)
276{
277 return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC);
278}
279
280
281
282
283
284
285unsigned int cpuidle_state_count(unsigned int cpu)
286{
287 char file[SYSFS_PATH_MAX];
288 struct stat statbuf;
289 int idlestates = 1;
290
291
292 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
293 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
294 return 0;
295
296 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
297 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
298 return 0;
299
300 while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
301 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
302 "cpu%u/cpuidle/state%d", cpu, idlestates);
303 idlestates++;
304 }
305 idlestates--;
306 return idlestates;
307}
308
309
310
311
312
313
314
315static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
316 size_t buflen)
317{
318 char path[SYSFS_PATH_MAX];
319
320 snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
321
322 return cpupower_read_sysfs(path, buf, buflen);
323}
324
325
326
327
328
329enum cpuidle_string {
330 CPUIDLE_GOVERNOR,
331 CPUIDLE_GOVERNOR_RO,
332 CPUIDLE_DRIVER,
333 MAX_CPUIDLE_STRING_FILES
334};
335
336static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
337 [CPUIDLE_GOVERNOR] = "current_governor",
338 [CPUIDLE_GOVERNOR_RO] = "current_governor_ro",
339 [CPUIDLE_DRIVER] = "current_driver",
340};
341
342
343static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
344{
345 char linebuf[MAX_LINE_LEN];
346 char *result;
347 unsigned int len;
348
349 if (which >= MAX_CPUIDLE_STRING_FILES)
350 return NULL;
351
352 len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
353 linebuf, sizeof(linebuf));
354 if (len == 0)
355 return NULL;
356
357 result = strdup(linebuf);
358 if (result == NULL)
359 return NULL;
360
361 if (result[strlen(result) - 1] == '\n')
362 result[strlen(result) - 1] = '\0';
363
364 return result;
365}
366
367char *cpuidle_get_governor(void)
368{
369 char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
370 if (!tmp)
371 return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
372 else
373 return tmp;
374}
375
376char *cpuidle_get_driver(void)
377{
378 return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
379}
380
381