File Index
Files and directories
Sign in
FILE

sysfs.c

File preview with syntax highlighting.

linux-6.19.14/tools/power/cpupower/utils/helpers/sysfs.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
  4 *  (C) 2011       Thomas Renninger <trenn@novell.com> 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
 18unsigned 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 */
 47int 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 */
103unsigned 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 */
124unsigned 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 */
156static
157unsigned 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
186enum idlestate_value {
187	IDLESTATE_USAGE,
188	IDLESTATE_POWER,
189	IDLESTATE_LATENCY,
190	IDLESTATE_TIME,
191	IDLESTATE_DISABLE,
192	MAX_IDLESTATE_VALUE_FILES
193};
194
195static 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
203static 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
231enum idlestate_string {
232	IDLESTATE_DESC,
233	IDLESTATE_NAME,
234	MAX_IDLESTATE_STRING_FILES
235};
236
237static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
238	[IDLESTATE_DESC] = "desc",
239	[IDLESTATE_NAME] = "name",
240};
241
242
243static 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 */
277int 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 */
298int 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
321unsigned 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
327unsigned 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
333unsigned 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
339char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate)
340{
341	return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME);
342}
343
344char *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 */
354unsigned 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 */
384static 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
398enum cpuidle_string {
399	CPUIDLE_GOVERNOR,
400	CPUIDLE_GOVERNOR_RO,
401	CPUIDLE_DRIVER,
402	MAX_CPUIDLE_STRING_FILES
403};
404
405static 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
412static 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
436char *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
445char *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 */
457int 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 */
468int sysfs_set_sched(const char *smt_mc, int val)
469{
470	return -ENODEV;
471}
~/ Read-only file preview.