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