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 
14 #include "helpers/sysfs.h"
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 				sysfs_get_idlestate_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 				sysfs_get_idlestate_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 static struct cpuidle_monitor *cpuidle_register(void)
130 {
131 	int num;
132 	char *tmp;
133 
134 	/* Assume idle state count is the same for all CPUs */
135 	cpuidle_sysfs_monitor.hw_states_num = sysfs_get_idlestate_count(0);
136 
137 	if (cpuidle_sysfs_monitor.hw_states_num <= 0)
138 		return NULL;
139 
140 	for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num++) {
141 		tmp = sysfs_get_idlestate_name(0, num);
142 		if (tmp == NULL)
143 			continue;
144 
145 		fix_up_intel_idle_driver_name(tmp, num);
146 		strncpy(cpuidle_cstates[num].name, tmp, CSTATE_NAME_LEN - 1);
147 		free(tmp);
148 
149 		tmp = sysfs_get_idlestate_desc(0, num);
150 		if (tmp == NULL)
151 			continue;
152 		strncpy(cpuidle_cstates[num].desc, tmp,	CSTATE_DESC_LEN - 1);
153 		free(tmp);
154 
155 		cpuidle_cstates[num].range = RANGE_THREAD;
156 		cpuidle_cstates[num].id = num;
157 		cpuidle_cstates[num].get_count_percent =
158 			cpuidle_get_count_percent;
159 	};
160 
161 	/* Free this at program termination */
162 	previous_count = malloc(sizeof(long long *) * cpu_count);
163 	current_count = malloc(sizeof(long long *) * cpu_count);
164 	for (num = 0; num < cpu_count; num++) {
165 		previous_count[num] = malloc(sizeof(long long) *
166 					cpuidle_sysfs_monitor.hw_states_num);
167 		current_count[num] = malloc(sizeof(long long) *
168 					cpuidle_sysfs_monitor.hw_states_num);
169 	}
170 
171 	cpuidle_sysfs_monitor.name_len = strlen(cpuidle_sysfs_monitor.name);
172 	return &cpuidle_sysfs_monitor;
173 }
174 
175 void cpuidle_unregister(void)
176 {
177 	int num;
178 
179 	for (num = 0; num < cpu_count; num++) {
180 		free(previous_count[num]);
181 		free(current_count[num]);
182 	}
183 	free(previous_count);
184 	free(current_count);
185 }
186 
187 struct cpuidle_monitor cpuidle_sysfs_monitor = {
188 	.name			= "Idle_Stats",
189 	.hw_states		= cpuidle_cstates,
190 	.start			= cpuidle_start,
191 	.stop			= cpuidle_stop,
192 	.do_register		= cpuidle_register,
193 	.unregister		= cpuidle_unregister,
194 	.needs_root		= 0,
195 	.overflow_s		= UINT_MAX,
196 };
197