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