File Index
Files and directories
Sign in
FILE

cpuidle.c

File preview with syntax highlighting.

linux-6.19.14/tools/power/cpupower/lib/cpuidle.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 "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 */
 28static
 29unsigned 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 */
 50static
 51unsigned 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 */
 85static
 86unsigned 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
115enum 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
125static 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
134static
135unsigned 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	errno = 0;
154	value = strtoull(linebuf, &endp, 0);
155
156	if (endp == linebuf || errno == ERANGE)
157		return 0;
158
159	return value;
160}
161
162/* read access to files which contain one string */
163
164enum idlestate_string {
165	IDLESTATE_DESC,
166	IDLESTATE_NAME,
167	MAX_IDLESTATE_STRING_FILES
168};
169
170static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
171	[IDLESTATE_DESC] = "desc",
172	[IDLESTATE_NAME] = "name",
173};
174
175
176static char *cpuidle_state_get_one_string(unsigned int cpu,
177					unsigned int idlestate,
178					enum idlestate_string which)
179{
180	char linebuf[MAX_LINE_LEN];
181	char *result;
182	unsigned int len;
183
184	if (which >= MAX_IDLESTATE_STRING_FILES)
185		return NULL;
186
187	len = cpuidle_state_read_file(cpu, idlestate,
188				      idlestate_string_files[which],
189				      linebuf, sizeof(linebuf));
190	if (len == 0)
191		return NULL;
192
193	result = strdup(linebuf);
194	if (result == NULL)
195		return NULL;
196
197	if (result[strlen(result) - 1] == '\n')
198		result[strlen(result) - 1] = '\0';
199
200	return result;
201}
202
203/*
204 * Returns:
205 *    1  if disabled
206 *    0  if enabled
207 *    -1 if idlestate is not available
208 *    -2 if disabling is not supported by the kernel
209 */
210int cpuidle_is_state_disabled(unsigned int cpu,
211				unsigned int idlestate)
212{
213	if (cpuidle_state_count(cpu) <= idlestate)
214		return -1;
215
216	if (!cpuidle_state_file_exists(cpu, idlestate,
217				 idlestate_value_files[IDLESTATE_DISABLE]))
218		return -2;
219	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);
220}
221
222/*
223 * Pass 1 as last argument to disable or 0 to enable the state
224 * Returns:
225 *    0  on success
226 *    negative values on error, for example:
227 *      -1 if idlestate is not available
228 *      -2 if disabling is not supported by the kernel
229 *      -3 No write access to disable/enable C-states
230 */
231int cpuidle_state_disable(unsigned int cpu,
232			    unsigned int idlestate,
233			    unsigned int disable)
234{
235	char value[SYSFS_PATH_MAX];
236	int bytes_written;
237	int len;
238
239	if (cpuidle_state_count(cpu) <= idlestate)
240		return -1;
241
242	if (!cpuidle_state_file_exists(cpu, idlestate,
243				 idlestate_value_files[IDLESTATE_DISABLE]))
244		return -2;
245
246	len = snprintf(value, SYSFS_PATH_MAX, "%u", disable);
247
248	bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable",
249						   value, len);
250	if (bytes_written)
251		return 0;
252	return -3;
253}
254
255unsigned long cpuidle_state_latency(unsigned int cpu,
256					  unsigned int idlestate)
257{
258	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
259}
260
261unsigned long cpuidle_state_residency(unsigned int cpu,
262					  unsigned int idlestate)
263{
264	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_RESIDENCY);
265}
266
267unsigned long cpuidle_state_usage(unsigned int cpu,
268					unsigned int idlestate)
269{
270	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
271}
272
273unsigned long long cpuidle_state_time(unsigned int cpu,
274					unsigned int idlestate)
275{
276	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME);
277}
278
279char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate)
280{
281	return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME);
282}
283
284char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate)
285{
286	return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC);
287}
288
289/*
290 * Returns number of supported C-states of CPU core cpu
291 * Negativ in error case
292 * Zero if cpuidle does not export any C-states
293 */
294unsigned int cpuidle_state_count(unsigned int cpu)
295{
296	char file[SYSFS_PATH_MAX];
297	struct stat statbuf;
298	int idlestates = 1;
299
300
301	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
302	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
303		return 0;
304
305	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
306	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
307		return 0;
308
309	while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
310		snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
311			 "cpu%u/cpuidle/state%d", cpu, idlestates);
312		idlestates++;
313	}
314	idlestates--;
315	return idlestates;
316}
317
318/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
319
320/*
321 * helper function to read file from /sys into given buffer
322 * fname is a relative path under "cpu/cpuidle/" dir
323 */
324static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
325					    size_t buflen)
326{
327	char path[SYSFS_PATH_MAX];
328
329	snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
330
331	return cpupower_read_sysfs(path, buf, buflen);
332}
333
334
335
336/* read access to files which contain one string */
337
338enum cpuidle_string {
339	CPUIDLE_GOVERNOR,
340	CPUIDLE_GOVERNOR_RO,
341	CPUIDLE_DRIVER,
342	MAX_CPUIDLE_STRING_FILES
343};
344
345static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
346	[CPUIDLE_GOVERNOR]	= "current_governor",
347	[CPUIDLE_GOVERNOR_RO]	= "current_governor_ro",
348	[CPUIDLE_DRIVER]	= "current_driver",
349};
350
351
352static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
353{
354	char linebuf[MAX_LINE_LEN];
355	char *result;
356	unsigned int len;
357
358	if (which >= MAX_CPUIDLE_STRING_FILES)
359		return NULL;
360
361	len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
362				linebuf, sizeof(linebuf));
363	if (len == 0)
364		return NULL;
365
366	result = strdup(linebuf);
367	if (result == NULL)
368		return NULL;
369
370	if (result[strlen(result) - 1] == '\n')
371		result[strlen(result) - 1] = '\0';
372
373	return result;
374}
375
376char *cpuidle_get_governor(void)
377{
378	char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
379	if (!tmp)
380		return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
381	else
382		return tmp;
383}
384
385char *cpuidle_get_driver(void)
386{
387	return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
388}
389/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
~/ Read-only file preview.