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 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 }; 215