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