cpupowerutils - cpufrequtils extended with quite some features
[linux-2.6.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
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         };
60         return 0;
61 }
62
63 static int cpuidle_stop(void)
64 {
65         int cpu, state;
66         struct timespec end_time;
67         clock_gettime(CLOCK_REALTIME, &end_time);
68         timediff = timespec_diff_us(start_time, end_time);
69
70         for (cpu = 0; cpu < cpu_count; cpu++) {
71                 for (state = 0; state < cpuidle_sysfs_monitor.hw_states_num;
72                      state++) {
73                         current_count[cpu][state] =
74                                 sysfs_get_idlestate_time(cpu, state);
75                         dprint("CPU %d - State: %d - Val: %llu\n",
76                                cpu, state, previous_count[cpu][state]);
77                 }
78         };
79         return 0;
80 }
81
82 void fix_up_intel_idle_driver_name(char *tmp, int num)
83 {
84         /* fix up cpuidle name for intel idle driver */
85         if (!strncmp(tmp, "NHM-", 4)) {
86                 switch(num) {
87                 case 1: strcpy(tmp, "C1");
88                         break;
89                 case 2: strcpy(tmp, "C3");
90                         break;
91                 case 3: strcpy(tmp, "C6");
92                         break;
93                 }
94         } else if (!strncmp(tmp, "SNB-", 4)) {
95                 switch(num) {
96                 case 1: strcpy(tmp, "C1");
97                         break;
98                 case 2: strcpy(tmp, "C3");
99                         break;
100                 case 3: strcpy(tmp, "C6");
101                         break;
102                 case 4: strcpy(tmp, "C7");
103                         break;
104                 }
105         } else if (!strncmp(tmp, "ATM-", 4)) {
106                 switch(num) {
107                 case 1: strcpy(tmp, "C1");
108                         break;
109                 case 2: strcpy(tmp, "C2");
110                         break;
111                 case 3: strcpy(tmp, "C4");
112                         break;
113                 case 4: strcpy(tmp, "C6");
114                         break;
115                 }
116         }
117 }
118
119 static struct cpuidle_monitor* cpuidle_register(void)
120 {
121         int num;
122         char *tmp;
123
124         /* Assume idle state count is the same for all CPUs */
125         cpuidle_sysfs_monitor.hw_states_num = sysfs_get_idlestate_count(0);
126
127         if (cpuidle_sysfs_monitor.hw_states_num == 0)
128                 return NULL;
129
130         for (num = 0; num < cpuidle_sysfs_monitor.hw_states_num; num ++) {
131                 tmp = sysfs_get_idlestate_name(0, num);
132                 if (tmp == NULL)
133                         continue;
134
135                 fix_up_intel_idle_driver_name(tmp, num);
136                 strncpy(cpuidle_cstates[num].name, tmp, CSTATE_NAME_LEN - 1);
137                 free(tmp);
138
139                 tmp = sysfs_get_idlestate_desc(0, num);
140                 if (tmp == NULL)
141                         continue;
142                 strncpy(cpuidle_cstates[num].desc, tmp, CSTATE_DESC_LEN - 1);
143                 free(tmp);
144
145                 cpuidle_cstates[num].range = RANGE_THREAD;
146                 cpuidle_cstates[num].id = num;
147                 cpuidle_cstates[num].get_count_percent = cpuidle_get_count_percent;
148         };
149
150         /* Free this at program termination */
151         previous_count = malloc(sizeof (long long*) * cpu_count);
152         current_count = malloc(sizeof (long long*) * cpu_count);
153         for (num = 0; num < cpu_count; num++) {
154                 previous_count[num] = malloc (sizeof(long long) *
155                                               cpuidle_sysfs_monitor.hw_states_num);
156                 current_count[num] = malloc (sizeof(long long) *
157                                              cpuidle_sysfs_monitor.hw_states_num);
158         }
159
160         cpuidle_sysfs_monitor.name_len = strlen(cpuidle_sysfs_monitor.name);
161         return &cpuidle_sysfs_monitor;
162 }
163
164 void cpuidle_unregister(void)
165 {
166         int num;
167
168         for (num = 0; num < cpu_count; num++) {
169                 free(previous_count[num]);
170                 free(current_count[num]);
171         }
172         free(previous_count);
173         free(current_count);
174 }
175
176 struct cpuidle_monitor cpuidle_sysfs_monitor = {
177         .name                   = "Idle_Stats",
178         .hw_states              = cpuidle_cstates,
179         .start                  = cpuidle_start,
180         .stop                   = cpuidle_stop,
181         .do_register            = cpuidle_register,
182         .unregister             = cpuidle_unregister,
183         .needs_root             = 0,
184         .overflow_s             = UINT_MAX,
185 };