Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/power/cpupower/lib/cpuidle.c
26295 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
237
if (cpuidle_state_count(cpu) <= idlestate)
238
return -1;
239
240
if (!cpuidle_state_file_exists(cpu, idlestate,
241
idlestate_value_files[IDLESTATE_DISABLE]))
242
return -2;
243
244
snprintf(value, SYSFS_PATH_MAX, "%u", disable);
245
246
bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable",
247
value, sizeof(disable));
248
if (bytes_written)
249
return 0;
250
return -3;
251
}
252
253
unsigned long cpuidle_state_latency(unsigned int cpu,
254
unsigned int idlestate)
255
{
256
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
257
}
258
259
unsigned long cpuidle_state_residency(unsigned int cpu,
260
unsigned int idlestate)
261
{
262
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_RESIDENCY);
263
}
264
265
unsigned long cpuidle_state_usage(unsigned int cpu,
266
unsigned int idlestate)
267
{
268
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
269
}
270
271
unsigned long long cpuidle_state_time(unsigned int cpu,
272
unsigned int idlestate)
273
{
274
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME);
275
}
276
277
char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate)
278
{
279
return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME);
280
}
281
282
char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate)
283
{
284
return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC);
285
}
286
287
/*
288
* Returns number of supported C-states of CPU core cpu
289
* Negativ in error case
290
* Zero if cpuidle does not export any C-states
291
*/
292
unsigned int cpuidle_state_count(unsigned int cpu)
293
{
294
char file[SYSFS_PATH_MAX];
295
struct stat statbuf;
296
int idlestates = 1;
297
298
299
snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
300
if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
301
return 0;
302
303
snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
304
if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
305
return 0;
306
307
while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
308
snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
309
"cpu%u/cpuidle/state%d", cpu, idlestates);
310
idlestates++;
311
}
312
idlestates--;
313
return idlestates;
314
}
315
316
/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
317
318
/*
319
* helper function to read file from /sys into given buffer
320
* fname is a relative path under "cpu/cpuidle/" dir
321
*/
322
static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
323
size_t buflen)
324
{
325
char path[SYSFS_PATH_MAX];
326
327
snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
328
329
return cpupower_read_sysfs(path, buf, buflen);
330
}
331
332
333
334
/* read access to files which contain one string */
335
336
enum cpuidle_string {
337
CPUIDLE_GOVERNOR,
338
CPUIDLE_GOVERNOR_RO,
339
CPUIDLE_DRIVER,
340
MAX_CPUIDLE_STRING_FILES
341
};
342
343
static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
344
[CPUIDLE_GOVERNOR] = "current_governor",
345
[CPUIDLE_GOVERNOR_RO] = "current_governor_ro",
346
[CPUIDLE_DRIVER] = "current_driver",
347
};
348
349
350
static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
351
{
352
char linebuf[MAX_LINE_LEN];
353
char *result;
354
unsigned int len;
355
356
if (which >= MAX_CPUIDLE_STRING_FILES)
357
return NULL;
358
359
len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
360
linebuf, sizeof(linebuf));
361
if (len == 0)
362
return NULL;
363
364
result = strdup(linebuf);
365
if (result == NULL)
366
return NULL;
367
368
if (result[strlen(result) - 1] == '\n')
369
result[strlen(result) - 1] = '\0';
370
371
return result;
372
}
373
374
char *cpuidle_get_governor(void)
375
{
376
char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
377
if (!tmp)
378
return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
379
else
380
return tmp;
381
}
382
383
char *cpuidle_get_driver(void)
384
{
385
return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
386
}
387
/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
388
389