GNU Linux-libre 4.19.263-gnu1
[releases.git] / tools / power / cpupower / utils / idle_monitor / cpuidle_sysfs.c
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 static 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 };