Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/power/cpupower/utils/helpers/sysfs.c
26299 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 "helpers/sysfs.h"
17
18
unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
19
{
20
int fd;
21
ssize_t numread;
22
23
fd = open(path, O_RDONLY);
24
if (fd == -1)
25
return 0;
26
27
numread = read(fd, buf, buflen - 1);
28
if (numread < 1) {
29
close(fd);
30
return 0;
31
}
32
33
buf[numread] = '\0';
34
close(fd);
35
36
return (unsigned int) numread;
37
}
38
39
/*
40
* Detect whether a CPU is online
41
*
42
* Returns:
43
* 1 -> if CPU is online
44
* 0 -> if CPU is offline
45
* negative errno values in error case
46
*/
47
int sysfs_is_cpu_online(unsigned int cpu)
48
{
49
char path[SYSFS_PATH_MAX];
50
int fd;
51
ssize_t numread;
52
unsigned long long value;
53
char linebuf[MAX_LINE_LEN];
54
char *endp;
55
struct stat statbuf;
56
57
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
58
59
if (stat(path, &statbuf) != 0)
60
return 0;
61
62
/*
63
* kernel without CONFIG_HOTPLUG_CPU
64
* -> cpuX directory exists, but not cpuX/online file
65
*/
66
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
67
if (stat(path, &statbuf) != 0)
68
return 1;
69
70
fd = open(path, O_RDONLY);
71
if (fd == -1)
72
return -errno;
73
74
numread = read(fd, linebuf, MAX_LINE_LEN - 1);
75
if (numread < 1) {
76
close(fd);
77
return -EIO;
78
}
79
linebuf[numread] = '\0';
80
close(fd);
81
82
value = strtoull(linebuf, &endp, 0);
83
if (value > 1)
84
return -EINVAL;
85
86
return value;
87
}
88
89
/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
90
91
92
/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
93
94
/*
95
* helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir
96
* exists.
97
* For example the functionality to disable c-states was introduced in later
98
* kernel versions, this function can be used to explicitly check for this
99
* feature.
100
*
101
* returns 1 if the file exists, 0 otherwise.
102
*/
103
unsigned int sysfs_idlestate_file_exists(unsigned int cpu,
104
unsigned int idlestate,
105
const char *fname)
106
{
107
char path[SYSFS_PATH_MAX];
108
struct stat statbuf;
109
110
111
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
112
cpu, idlestate, fname);
113
if (stat(path, &statbuf) != 0)
114
return 0;
115
return 1;
116
}
117
118
/*
119
* helper function to read file from /sys into given buffer
120
* fname is a relative path under "cpuX/cpuidle/stateX/" dir
121
* cstates starting with 0, C0 is not counted as cstate.
122
* This means if you want C1 info, pass 0 as idlestate param
123
*/
124
unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate,
125
const char *fname, char *buf, size_t buflen)
126
{
127
char path[SYSFS_PATH_MAX];
128
int fd;
129
ssize_t numread;
130
131
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
132
cpu, idlestate, fname);
133
134
fd = open(path, O_RDONLY);
135
if (fd == -1)
136
return 0;
137
138
numread = read(fd, buf, buflen - 1);
139
if (numread < 1) {
140
close(fd);
141
return 0;
142
}
143
144
buf[numread] = '\0';
145
close(fd);
146
147
return (unsigned int) numread;
148
}
149
150
/*
151
* helper function to write a new value to a /sys file
152
* fname is a relative path under "../cpuX/cpuidle/cstateY/" dir
153
*
154
* Returns the number of bytes written or 0 on error
155
*/
156
static
157
unsigned int sysfs_idlestate_write_file(unsigned int cpu,
158
unsigned int idlestate,
159
const char *fname,
160
const char *value, size_t len)
161
{
162
char path[SYSFS_PATH_MAX];
163
int fd;
164
ssize_t numwrite;
165
166
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
167
cpu, idlestate, fname);
168
169
fd = open(path, O_WRONLY);
170
if (fd == -1)
171
return 0;
172
173
numwrite = write(fd, value, len);
174
if (numwrite < 1) {
175
close(fd);
176
return 0;
177
}
178
179
close(fd);
180
181
return (unsigned int) numwrite;
182
}
183
184
/* read access to files which contain one numeric value */
185
186
enum idlestate_value {
187
IDLESTATE_USAGE,
188
IDLESTATE_POWER,
189
IDLESTATE_LATENCY,
190
IDLESTATE_TIME,
191
IDLESTATE_DISABLE,
192
MAX_IDLESTATE_VALUE_FILES
193
};
194
195
static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {
196
[IDLESTATE_USAGE] = "usage",
197
[IDLESTATE_POWER] = "power",
198
[IDLESTATE_LATENCY] = "latency",
199
[IDLESTATE_TIME] = "time",
200
[IDLESTATE_DISABLE] = "disable",
201
};
202
203
static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu,
204
unsigned int idlestate,
205
enum idlestate_value which)
206
{
207
unsigned long long value;
208
unsigned int len;
209
char linebuf[MAX_LINE_LEN];
210
char *endp;
211
212
if (which >= MAX_IDLESTATE_VALUE_FILES)
213
return 0;
214
215
len = sysfs_idlestate_read_file(cpu, idlestate,
216
idlestate_value_files[which],
217
linebuf, sizeof(linebuf));
218
if (len == 0)
219
return 0;
220
221
value = strtoull(linebuf, &endp, 0);
222
223
if (endp == linebuf || errno == ERANGE)
224
return 0;
225
226
return value;
227
}
228
229
/* read access to files which contain one string */
230
231
enum idlestate_string {
232
IDLESTATE_DESC,
233
IDLESTATE_NAME,
234
MAX_IDLESTATE_STRING_FILES
235
};
236
237
static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
238
[IDLESTATE_DESC] = "desc",
239
[IDLESTATE_NAME] = "name",
240
};
241
242
243
static char *sysfs_idlestate_get_one_string(unsigned int cpu,
244
unsigned int idlestate,
245
enum idlestate_string which)
246
{
247
char linebuf[MAX_LINE_LEN];
248
char *result;
249
unsigned int len;
250
251
if (which >= MAX_IDLESTATE_STRING_FILES)
252
return NULL;
253
254
len = sysfs_idlestate_read_file(cpu, idlestate,
255
idlestate_string_files[which],
256
linebuf, sizeof(linebuf));
257
if (len == 0)
258
return NULL;
259
260
result = strdup(linebuf);
261
if (result == NULL)
262
return NULL;
263
264
if (result[strlen(result) - 1] == '\n')
265
result[strlen(result) - 1] = '\0';
266
267
return result;
268
}
269
270
/*
271
* Returns:
272
* 1 if disabled
273
* 0 if enabled
274
* -1 if idlestate is not available
275
* -2 if disabling is not supported by the kernel
276
*/
277
int sysfs_is_idlestate_disabled(unsigned int cpu,
278
unsigned int idlestate)
279
{
280
if (sysfs_get_idlestate_count(cpu) <= idlestate)
281
return -1;
282
283
if (!sysfs_idlestate_file_exists(cpu, idlestate,
284
idlestate_value_files[IDLESTATE_DISABLE]))
285
return -2;
286
return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);
287
}
288
289
/*
290
* Pass 1 as last argument to disable or 0 to enable the state
291
* Returns:
292
* 0 on success
293
* negative values on error, for example:
294
* -1 if idlestate is not available
295
* -2 if disabling is not supported by the kernel
296
* -3 No write access to disable/enable C-states
297
*/
298
int sysfs_idlestate_disable(unsigned int cpu,
299
unsigned int idlestate,
300
unsigned int disable)
301
{
302
char value[SYSFS_PATH_MAX];
303
int bytes_written;
304
305
if (sysfs_get_idlestate_count(cpu) <= idlestate)
306
return -1;
307
308
if (!sysfs_idlestate_file_exists(cpu, idlestate,
309
idlestate_value_files[IDLESTATE_DISABLE]))
310
return -2;
311
312
snprintf(value, SYSFS_PATH_MAX, "%u", disable);
313
314
bytes_written = sysfs_idlestate_write_file(cpu, idlestate, "disable",
315
value, sizeof(disable));
316
if (bytes_written)
317
return 0;
318
return -3;
319
}
320
321
unsigned long sysfs_get_idlestate_latency(unsigned int cpu,
322
unsigned int idlestate)
323
{
324
return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
325
}
326
327
unsigned long sysfs_get_idlestate_usage(unsigned int cpu,
328
unsigned int idlestate)
329
{
330
return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
331
}
332
333
unsigned long long sysfs_get_idlestate_time(unsigned int cpu,
334
unsigned int idlestate)
335
{
336
return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME);
337
}
338
339
char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate)
340
{
341
return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME);
342
}
343
344
char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate)
345
{
346
return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC);
347
}
348
349
/*
350
* Returns number of supported C-states of CPU core cpu
351
* Negativ in error case
352
* Zero if cpuidle does not export any C-states
353
*/
354
unsigned int sysfs_get_idlestate_count(unsigned int cpu)
355
{
356
char file[SYSFS_PATH_MAX];
357
struct stat statbuf;
358
int idlestates = 1;
359
360
361
snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
362
if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
363
return 0;
364
365
snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
366
if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
367
return 0;
368
369
while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
370
snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
371
"cpu%u/cpuidle/state%d", cpu, idlestates);
372
idlestates++;
373
}
374
idlestates--;
375
return idlestates;
376
}
377
378
/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
379
380
/*
381
* helper function to read file from /sys into given buffer
382
* fname is a relative path under "cpu/cpuidle/" dir
383
*/
384
static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
385
size_t buflen)
386
{
387
char path[SYSFS_PATH_MAX];
388
389
snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
390
391
return sysfs_read_file(path, buf, buflen);
392
}
393
394
395
396
/* read access to files which contain one string */
397
398
enum cpuidle_string {
399
CPUIDLE_GOVERNOR,
400
CPUIDLE_GOVERNOR_RO,
401
CPUIDLE_DRIVER,
402
MAX_CPUIDLE_STRING_FILES
403
};
404
405
static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
406
[CPUIDLE_GOVERNOR] = "current_governor",
407
[CPUIDLE_GOVERNOR_RO] = "current_governor_ro",
408
[CPUIDLE_DRIVER] = "current_driver",
409
};
410
411
412
static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
413
{
414
char linebuf[MAX_LINE_LEN];
415
char *result;
416
unsigned int len;
417
418
if (which >= MAX_CPUIDLE_STRING_FILES)
419
return NULL;
420
421
len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
422
linebuf, sizeof(linebuf));
423
if (len == 0)
424
return NULL;
425
426
result = strdup(linebuf);
427
if (result == NULL)
428
return NULL;
429
430
if (result[strlen(result) - 1] == '\n')
431
result[strlen(result) - 1] = '\0';
432
433
return result;
434
}
435
436
char *sysfs_get_cpuidle_governor(void)
437
{
438
char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
439
if (!tmp)
440
return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
441
else
442
return tmp;
443
}
444
445
char *sysfs_get_cpuidle_driver(void)
446
{
447
return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
448
}
449
/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
450
451
/*
452
* Get sched_mc or sched_smt settings
453
* Pass "mc" or "smt" as argument
454
*
455
* Returns negative value on failure
456
*/
457
int sysfs_get_sched(const char *smt_mc)
458
{
459
return -ENODEV;
460
}
461
462
/*
463
* Get sched_mc or sched_smt settings
464
* Pass "mc" or "smt" as argument
465
*
466
* Returns negative value on failure
467
*/
468
int sysfs_set_sched(const char *smt_mc, int val)
469
{
470
return -ENODEV;
471
}
472
473