1 /*
2  *  (C) 2010,2011       Thomas Renninger <trenn@suse.de>, Novell Inc
3  *
4  *  Licensed under the terms of the GNU GPL License version 2.
5  *
6  */
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdint.h>
11 #include <string.h>
12 #include <limits.h>
13 #include <cpuidle.h>
14 
15 #include "helpers/helpers.h"
16 #include "idle_monitor/cpupower-monitor.h"
17 
18 #define CPUIDLE_STATES_MAX 10
19 static cstate_t cpuidle_cstates[CPUIDLE_STATES_MAX];
20 struct cpuidle_monitor cpuidle_sysfs_monitor;
21 
22 static unsigned long long **previous_count;
23 static unsigned long long **current_count;
24 struct timespec start_time;
25 static unsigned long long timediff;
26 
27 static int cpuidle_get_count_percent(unsigned int id, double *percent,
28 				     unsigned int cpu)
29 {
30 	unsigned long long statediff = current_count[cpu][id]
31 		- previous_count[cpu][id];
32 	dprint("%s: - diff: %llu - percent: %f (%u)\n",
33 	       cpuidle_cstates[id].name, timediff, *percent, cpu);
34 
35 	if (timediff == 0)
36 		*percent = 0.0;
37 	else
38 		*percent = ((100.0 * statediff) / timediff);
39 
40 	dprint("%s: - timediff: %llu - statediff: %llu - percent: %f (%u)\n",
41 	       cpuidle_cstates[id].name, timediff, statediff, *percent, cpu);
42 
43 	return 0;
44 }
45 
46 static int cpuidle_start(void)
47 {
48 	int cpu, state;
49 	clock_gettime(CLOCK_REALTIME, &start_time);
50 	for (cpu = 0; cpu < cpu_count; cpu++) {
51 		for (state = 0; state < cpuidle_sysfs_monitor.hw_states_num;
52 		     state++) {
53 			previous_count[cpu][state] =
54 				cpuidle_state_time(cpu, state);
55 			dprint("CPU %d - State: %d - Val: %llu\n",
56 			       cpu, state, previous_count[cpu][state]);
57 		}
58 	};
59 	return 0;
60 }
61 
62 static int cpuidle_stop(void)
63 {
64 	int cpu, state;
65 	struct timespec end_time;
66 	clock_gettime(CLOCK_REALTIME, &end_time);
67 	timediff = timespec_diff_us(start_time, end_time);
68 
69 	for (cpu = 0; cpu < cpu_count; cpu++) {
70 		for (state = 0; state < cpuidle_sysfs_monitor.hw_states_num;
71 		     state++) {
72 			current_count[cpu][state] =
73 				cpuidle_state_time(cpu, state);
74 			dprint("CPU %d - State: %d - Val: %llu\n",
75 			       cpu, state, previous_count[cpu][state]);
76 		}
77 	};
78 	return 0;
79 }
80 
81 void fix_up_intel_idle_driver_name(char *tmp, int num)
82 {
83 	/* fix up cpuidle name for intel idle driver */
84 	if (!strncmp(tmp, "NHM-", 4)) {
85 		switch (num) {
86 		case 1:
87 			strcpy(tmp, "C1");
88 			break;
89 		case 2:
90 			strcpy(tmp, "C3");
91 			break;
92 		case 3:
93 			strcpy(tmp, "C6");
94 			break;
95 		}
96 	} else if (!strncmp(tmp, "SNB-", 4)) {
97 		switch (num) {
98 		case 1:
99 			strcpy(tmp, "C1");
100 			break;
101 		case 2:
102 			strcpy(tmp, "C3");
103 			break;
104 		case 3:
105 			strcpy(tmp, "C6");
106 			break;
107 		case 4:
108 			strcpy(tmp, "C7");
109 			break;
110 		}
111 	} else if (!strncmp(tmp, "ATM-", 4)) {
112 		switch (num) {
113 		case 1:
114 			strcpy(tmp, "C1");
115 			break;
116 		case 2:
117 			strcpy(tmp, "C2");
118 			break;
119 		case 3:
120 			strcpy(tmp, "C4");
121 			break;
122 		case 4:
123 			strcpy(tmp, "C6");
124 			break;
125 		}
126 	}
127 }
128 
129 #ifdef __powerpc__
130 void map_power_idle_state_name(char *tmp)
131 {
132 	if (!strncmp(tmp, "stop0_lite", CSTATE_NAME_LEN))
133 		strcpy(tmp, "stop0L");
134 	else if (!strncmp(tmp, "stop1_lite", CSTATE_NAME_LEN))
135 		strcpy(tmp, "stop1L");
136 	else if (!strncmp(tmp, "stop2_lite", CSTATE_NAME_LEN))
137 		strcpy(tmp, "stop2L");
138 }
139 #else
140 void map_power_idle_state_name(char *tmp) { }
141 #endif
142 
143 static struct cpuidle_monitor *cpuidle_register(void)
144 {
145 	int num;
146 	char *tmp;
147 	int this_cpu;
148 
149 	this_cpu = sched_getcpu();
150 
151 	/* Assume idle state count is the same for all CPUs */
152 	cpuidle_sysfs_monitor.hw_states_num = cpuidle_state_count(this_cpu);
153 
154 	if (cpuidle_sysfs_monitor.hw_states_num <= 0)
155 		return NULL;
156 
157 	for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) {
158 		tmp = cpuidle_state_name(this_cpu, num);
159 		if (tmp == NULL)
160 			continue;
161 
162 		map_power_idle_state_name(tmp);
163 		fix_up_intel_idle_driver_name(tmp, num);
164 		strncpy(cpuidle_cstates[num].name, tmp, CSTATE_NAME_LEN - 1);
165 		free(tmp);
166 
167 		tmp = cpuidle_state_desc(this_cpu, num);
168 		if (tmp == NULL)
169 			continue;
170 		strncpy(cpuidle_cstates[num].desc, tmp,	CSTATE_DESC_LEN - 1);
171 		free(tmp);
172 
173 		cpuidle_cstates[num].range = RANGE_THREAD;
174 		cpuidle_cstates[num].id = num;
175 		cpuidle_cstates[num].get_count_percent =
176 			cpuidle_get_count_percent;
177 	};
178 
179 	/* Free this at program termination */
180 	previous_count = malloc(sizeof(long long *) * cpu_count);
181 	current_count = malloc(sizeof(long long *) * cpu_count);
182 	for (num = 0; num < cpu_count; num++) {
183 		previous_count[num] = malloc(sizeof(long long) *
184 					cpuidle_sysfs_monitor.hw_states_num);
185 		current_count[num] = malloc(sizeof(long long) *
186 					cpuidle_sysfs_monitor.hw_states_num);
187 	}
188 
189 	cpuidle_sysfs_monitor.name_len = strlen(cpuidle_sysfs_monitor.name);
190 	return &cpuidle_sysfs_monitor;
191 }
192 
193 void cpuidle_unregister(void)
194 {
195 	int num;
196 
197 	for (num = 0; num < cpu_count; num++) {
198 		free(previous_count[num]);
199 		free(current_count[num]);
200 	}
201 	free(previous_count);
202 	free(current_count);
203 }
204 
205 struct cpuidle_monitor cpuidle_sysfs_monitor = {
206 	.name			= "Idle_Stats",
207 	.hw_states		= cpuidle_cstates,
208 	.start			= cpuidle_start,
209 	.stop			= cpuidle_stop,
210 	.do_register		= cpuidle_register,
211 	.unregister		= cpuidle_unregister,
212 	.needs_root		= 0,
213 	.overflow_s		= UINT_MAX,
214 };
215