Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/power/cpupower/lib/cpuidle.c
29269 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* (C) 2004-2009 Dominik Brodowski <[email protected]>
4
* (C) 2011 Thomas Renninger <[email protected]> Novell Inc.
5
*/
6
7
#include <stdio.h>
8
#include <errno.h>
9
#include <stdlib.h>
10
#include <string.h>
11
#include <sys/types.h>
12
#include <sys/stat.h>
13
#include <fcntl.h>
14
#include <unistd.h>
15
16
#include "cpuidle.h"
17
#include "cpupower_intern.h"
18
19
/*
20
* helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir
21
* exists.
22
* For example the functionality to disable c-states was introduced in later
23
* kernel versions, this function can be used to explicitly check for this
24
* feature.
25
*
26
* returns 1 if the file exists, 0 otherwise.
27
*/
28
static
29
unsigned int cpuidle_state_file_exists(unsigned int cpu,
30
unsigned int idlestate,
31
const char *fname)
32
{
33
char path[SYSFS_PATH_MAX];
34
struct stat statbuf;
35
36
37
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
38
cpu, idlestate, fname);
39
if (stat(path, &statbuf) != 0)
40
return 0;
41
return 1;
42
}
43
44
/*
45
* helper function to read file from /sys into given buffer
46
* fname is a relative path under "cpuX/cpuidle/stateX/" dir
47
* cstates starting with 0, C0 is not counted as cstate.
48
* This means if you want C1 info, pass 0 as idlestate param
49
*/
50
static
51
unsigned int cpuidle_state_read_file(unsigned int cpu,
52
unsigned int idlestate,
53
const char *fname, char *buf,
54
size_t buflen)
55
{
56
char path[SYSFS_PATH_MAX];
57
int fd;
58
ssize_t numread;
59
60
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
61
cpu, idlestate, fname);
62
63
fd = open(path, O_RDONLY);
64
if (fd == -1)
65
return 0;
66
67
numread = read(fd, buf, buflen - 1);
68
if (numread < 1) {
69
close(fd);
70
return 0;
71
}
72
73
buf[numread] = '\0';
74
close(fd);
75
76
return (unsigned int) numread;
77
}
78
79
/*
80
* helper function to write a new value to a /sys file
81
* fname is a relative path under "../cpuX/cpuidle/cstateY/" dir
82
*
83
* Returns the number of bytes written or 0 on error
84
*/
85
static
86
unsigned int cpuidle_state_write_file(unsigned int cpu,
87
unsigned int idlestate,
88
const char *fname,
89
const char *value, size_t len)
90
{
91
char path[SYSFS_PATH_MAX];
92
int fd;
93
ssize_t numwrite;
94
95
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
96
cpu, idlestate, fname);
97
98
fd = open(path, O_WRONLY);
99
if (fd == -1)
100
return 0;
101
102
numwrite = write(fd, value, len);
103
if (numwrite < 1) {
104
close(fd);
105
return 0;
106
}
107
108
close(fd);
109
110
return (unsigned int) numwrite;
111
}
112
113
/* read access to files which contain one numeric value */
114
115
enum idlestate_value {
116
IDLESTATE_USAGE,
117
IDLESTATE_POWER,
118
IDLESTATE_LATENCY,
119
IDLESTATE_RESIDENCY,
120
IDLESTATE_TIME,
121
IDLESTATE_DISABLE,
122
MAX_IDLESTATE_VALUE_FILES
123
};
124
125
static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {
126
[IDLESTATE_USAGE] = "usage",
127
[IDLESTATE_POWER] = "power",
128
[IDLESTATE_LATENCY] = "latency",
129
[IDLESTATE_RESIDENCY] = "residency",
130
[IDLESTATE_TIME] = "time",
131
[IDLESTATE_DISABLE] = "disable",
132
};
133
134
static
135
unsigned long long cpuidle_state_get_one_value(unsigned int cpu,
136
unsigned int idlestate,
137
enum idlestate_value which)
138
{
139
unsigned long long value;
140
unsigned int len;
141
char linebuf[MAX_LINE_LEN];
142
char *endp;
143
144
if (which >= MAX_IDLESTATE_VALUE_FILES)
145
return 0;
146
147
len = cpuidle_state_read_file(cpu, idlestate,
148
idlestate_value_files[which],
149
linebuf, sizeof(linebuf));
150
if (len == 0)
151
return 0;
152
153
value = strtoull(linebuf, &endp, 0);
154
155
if (endp == linebuf || errno == ERANGE)
156
return 0;
157
158
return value;
159
}
160
161
/* read access to files which contain one string */
162
163
enum idlestate_string {
164
IDLESTATE_DESC,
165
IDLESTATE_NAME,
166
MAX_IDLESTATE_STRING_FILES
167
};
168
169
static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
170
[IDLESTATE_DESC] = "desc",
171
[IDLESTATE_NAME] = "name",
172
};
173
174
175
static char *cpuidle_state_get_one_string(unsigned int cpu,
176
unsigned int idlestate,
177
enum idlestate_string which)
178
{
179
char linebuf[MAX_LINE_LEN];
180
char *result;
181
unsigned int len;
182
183
if (which >= MAX_IDLESTATE_STRING_FILES)
184
return NULL;
185
186
len = cpuidle_state_read_file(cpu, idlestate,
187
idlestate_string_files[which],
188
linebuf, sizeof(linebuf));
189
if (len == 0)
190
return NULL;
191
192
result = strdup(linebuf);
193
if (result == NULL)
194
return NULL;
195
196
if (result[strlen(result) - 1] == '\n')
197
result[strlen(result) - 1] = '\0';
198
199
return result;
200
}
201
202
/*
203
* Returns:
204
* 1 if disabled
205
* 0 if enabled
206
* -1 if idlestate is not available
207
* -2 if disabling is not supported by the kernel
208
*/
209
int cpuidle_is_state_disabled(unsigned int cpu,
210
unsigned int idlestate)
211
{
212
if (cpuidle_state_count(cpu) <= idlestate)
213
return -1;
214
215
if (!cpuidle_state_file_exists(cpu, idlestate,
216
idlestate_value_files[IDLESTATE_DISABLE]))
217
return -2;
218
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);
219
}
220
221
/*
222
* Pass 1 as last argument to disable or 0 to enable the state
223
* Returns:
224
* 0 on success
225
* negative values on error, for example:
226
* -1 if idlestate is not available
227
* -2 if disabling is not supported by the kernel
228
* -3 No write access to disable/enable C-states
229
*/
230
int cpuidle_state_disable(unsigned int cpu,
231
unsigned int idlestate,
232
unsigned int disable)
233
{
234
char value[SYSFS_PATH_MAX];
235
int bytes_written;
236
int len;
237
238
if (cpuidle_state_count(cpu) <= idlestate)
239
return -1;
240
241
if (!cpuidle_state_file_exists(cpu, idlestate,
242
idlestate_value_files[IDLESTATE_DISABLE]))
243
return -2;
244
245
len = snprintf(value, SYSFS_PATH_MAX, "%u", disable);
246
247
bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable",
248
value, len);
249
if (bytes_written)
250
return 0;
251
return -3;
252
}
253
254
unsigned long cpuidle_state_latency(unsigned int cpu,
255
unsigned int idlestate)
256
{
257
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
258
}
259
260
unsigned long cpuidle_state_residency(unsigned int cpu,
261
unsigned int idlestate)
262
{
263
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_RESIDENCY);
264
}
265
266
unsigned long cpuidle_state_usage(unsigned int cpu,
267
unsigned int idlestate)
268
{
269
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
270
}
271
272
unsigned long long cpuidle_state_time(unsigned int cpu,
273
unsigned int idlestate)
274
{
275
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME);
276
}
277
278
char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate)
279
{
280
return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME);
281
}
282
283
char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate)
284
{
285
return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC);
286
}
287
288
/*
289
* Returns number of supported C-states of CPU core cpu
290
* Negativ in error case
291
* Zero if cpuidle does not export any C-states
292
*/
293
unsigned int cpuidle_state_count(unsigned int cpu)
294
{
295
char file[SYSFS_PATH_MAX];
296
struct stat statbuf;
297
int idlestates = 1;
298
299
300
snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
301
if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
302
return 0;
303
304
snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
305
if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
306
return 0;
307
308
while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
309
snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
310
"cpu%u/cpuidle/state%d", cpu, idlestates);
311
idlestates++;
312
}
313
idlestates--;
314
return idlestates;
315
}
316
317
/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
318
319
/*
320
* helper function to read file from /sys into given buffer
321
* fname is a relative path under "cpu/cpuidle/" dir
322
*/
323
static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
324
size_t buflen)
325
{
326
char path[SYSFS_PATH_MAX];
327
328
snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
329
330
return cpupower_read_sysfs(path, buf, buflen);
331
}
332
333
334
335
/* read access to files which contain one string */
336
337
enum cpuidle_string {
338
CPUIDLE_GOVERNOR,
339
CPUIDLE_GOVERNOR_RO,
340
CPUIDLE_DRIVER,
341
MAX_CPUIDLE_STRING_FILES
342
};
343
344
static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
345
[CPUIDLE_GOVERNOR] = "current_governor",
346
[CPUIDLE_GOVERNOR_RO] = "current_governor_ro",
347
[CPUIDLE_DRIVER] = "current_driver",
348
};
349
350
351
static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
352
{
353
char linebuf[MAX_LINE_LEN];
354
char *result;
355
unsigned int len;
356
357
if (which >= MAX_CPUIDLE_STRING_FILES)
358
return NULL;
359
360
len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
361
linebuf, sizeof(linebuf));
362
if (len == 0)
363
return NULL;
364
365
result = strdup(linebuf);
366
if (result == NULL)
367
return NULL;
368
369
if (result[strlen(result) - 1] == '\n')
370
result[strlen(result) - 1] = '\0';
371
372
return result;
373
}
374
375
char *cpuidle_get_governor(void)
376
{
377
char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
378
if (!tmp)
379
return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
380
else
381
return tmp;
382
}
383
384
char *cpuidle_get_driver(void)
385
{
386
return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
387
}
388
/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
389
390