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