1 /*
2  *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
3  *  (C) 2010       Thomas Renninger <trenn@suse.de>
4  *
5  *  Licensed under the terms of the GNU GPL License version 2.
6  */
7 
8 
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <errno.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <getopt.h>
15 #include <cpufreq.h>
16 
17 #include "helpers/helpers.h"
18 #include "helpers/sysfs.h"
19 #include "helpers/bitmask.h"
20 
21 #define LINE_LEN 10
22 
23 static void cpuidle_cpu_output(unsigned int cpu, int verbose)
24 {
25 	int idlestates, idlestate;
26 	char *tmp;
27 
28 	printf(_ ("Analyzing CPU %d:\n"), cpu);
29 
30 	idlestates = sysfs_get_idlestate_count(cpu);
31 	if (idlestates == 0) {
32 		printf(_("CPU %u: No idle states\n"), cpu);
33 		return;
34 	} else if (idlestates <= 0) {
35 		printf(_("CPU %u: Can't read idle state info\n"), cpu);
36 		return;
37 	}
38 	tmp = sysfs_get_idlestate_name(cpu, idlestates - 1);
39 	if (!tmp) {
40 		printf(_("Could not determine max idle state %u\n"),
41 		       idlestates - 1);
42 		return;
43 	}
44 
45 	printf(_("Number of idle states: %d\n"), idlestates);
46 
47 	printf(_("Available idle states:"));
48 	for (idlestate = 1; idlestate < idlestates; idlestate++) {
49 		tmp = sysfs_get_idlestate_name(cpu, idlestate);
50 		if (!tmp)
51 			continue;
52 		printf(" %s", tmp);
53 		free(tmp);
54 	}
55 	printf("\n");
56 
57 	if (!verbose)
58 		return;
59 
60 	for (idlestate = 1; idlestate < idlestates; idlestate++) {
61 		tmp = sysfs_get_idlestate_name(cpu, idlestate);
62 		if (!tmp)
63 			continue;
64 		printf("%s:\n", tmp);
65 		free(tmp);
66 
67 		tmp = sysfs_get_idlestate_desc(cpu, idlestate);
68 		if (!tmp)
69 			continue;
70 		printf(_("Flags/Description: %s\n"), tmp);
71 		free(tmp);
72 
73 		printf(_("Latency: %lu\n"),
74 		       sysfs_get_idlestate_latency(cpu, idlestate));
75 		printf(_("Usage: %lu\n"),
76 		       sysfs_get_idlestate_usage(cpu, idlestate));
77 		printf(_("Duration: %llu\n"),
78 		       sysfs_get_idlestate_time(cpu, idlestate));
79 	}
80 	printf("\n");
81 }
82 
83 static void cpuidle_general_output(void)
84 {
85 	char *tmp;
86 
87 	tmp = sysfs_get_cpuidle_driver();
88 	if (!tmp) {
89 		printf(_("Could not determine cpuidle driver\n"));
90 		return;
91 	}
92 
93 	printf(_("CPUidle driver: %s\n"), tmp);
94 	free(tmp);
95 
96 	tmp = sysfs_get_cpuidle_governor();
97 	if (!tmp) {
98 		printf(_("Could not determine cpuidle governor\n"));
99 		return;
100 	}
101 
102 	printf(_("CPUidle governor: %s\n"), tmp);
103 	free(tmp);
104 }
105 
106 static void proc_cpuidle_cpu_output(unsigned int cpu)
107 {
108 	long max_allowed_cstate = 2000000000;
109 	int cstates, cstate;
110 
111 	cstates = sysfs_get_idlestate_count(cpu);
112 	if (cstates == 0) {
113 		/*
114 		 * Go on and print same useless info as you'd see with
115 		 * cat /proc/acpi/processor/../power
116 		 *	printf(_("CPU %u: No C-states available\n"), cpu);
117 		 *	return;
118 		 */
119 	} else if (cstates <= 0) {
120 		printf(_("CPU %u: Can't read C-state info\n"), cpu);
121 		return;
122 	}
123 	/* printf("Cstates: %d\n", cstates); */
124 
125 	printf(_("active state:            C0\n"));
126 	printf(_("max_cstate:              C%u\n"), cstates-1);
127 	printf(_("maximum allowed latency: %lu usec\n"), max_allowed_cstate);
128 	printf(_("states:\t\n"));
129 	for (cstate = 1; cstate < cstates; cstate++) {
130 		printf(_("    C%d:                  "
131 			 "type[C%d] "), cstate, cstate);
132 		printf(_("promotion[--] demotion[--] "));
133 		printf(_("latency[%03lu] "),
134 		       sysfs_get_idlestate_latency(cpu, cstate));
135 		printf(_("usage[%08lu] "),
136 		       sysfs_get_idlestate_usage(cpu, cstate));
137 		printf(_("duration[%020Lu] \n"),
138 		       sysfs_get_idlestate_time(cpu, cstate));
139 	}
140 }
141 
142 static struct option info_opts[] = {
143 	{ .name = "silent",	.has_arg = no_argument,	.flag = NULL,	.val = 's'},
144 	{ .name = "proc",	.has_arg = no_argument,	.flag = NULL,	.val = 'o'},
145 	{ },
146 };
147 
148 static inline void cpuidle_exit(int fail)
149 {
150 	exit(EXIT_FAILURE);
151 }
152 
153 int cmd_idle_info(int argc, char **argv)
154 {
155 	extern char *optarg;
156 	extern int optind, opterr, optopt;
157 	int ret = 0, cont = 1, output_param = 0, verbose = 1;
158 	unsigned int cpu = 0;
159 
160 	do {
161 		ret = getopt_long(argc, argv, "os", info_opts, NULL);
162 		if (ret == -1)
163 			break;
164 		switch (ret) {
165 		case '?':
166 			output_param = '?';
167 			cont = 0;
168 			break;
169 		case 's':
170 			verbose = 0;
171 			break;
172 		case -1:
173 			cont = 0;
174 			break;
175 		case 'o':
176 			if (output_param) {
177 				output_param = -1;
178 				cont = 0;
179 				break;
180 			}
181 			output_param = ret;
182 			break;
183 		}
184 	} while (cont);
185 
186 	switch (output_param) {
187 	case -1:
188 		printf(_("You can't specify more than one "
189 			 "output-specific argument\n"));
190 		cpuidle_exit(EXIT_FAILURE);
191 	case '?':
192 		printf(_("invalid or unknown argument\n"));
193 		cpuidle_exit(EXIT_FAILURE);
194 	}
195 
196 	/* Default is: show output of CPU 0 only */
197 	if (bitmask_isallclear(cpus_chosen))
198 		bitmask_setbit(cpus_chosen, 0);
199 
200 	if (output_param == 0)
201 		cpuidle_general_output();
202 
203 	for (cpu = bitmask_first(cpus_chosen);
204 	     cpu <= bitmask_last(cpus_chosen); cpu++) {
205 
206 		if (!bitmask_isbitset(cpus_chosen, cpu) ||
207 		    cpufreq_cpu_exists(cpu))
208 			continue;
209 
210 		switch (output_param) {
211 
212 		case 'o':
213 			proc_cpuidle_cpu_output(cpu);
214 			break;
215 		case 0:
216 			printf("\n");
217 			cpuidle_cpu_output(cpu, verbose);
218 			break;
219 		}
220 	}
221 	return EXIT_SUCCESS;
222 }
223