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