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 static 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 .flags.needs_root = 0, 211 .overflow_s = UINT_MAX, 212 }; 213