1 /* 2 * Copyright 2012-2013, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Yongcong Du <ycdu.vmcore@gmail.com> 7 * Paweł Dziepak, <pdziepak@quarnos.org> 8 */ 9 10 11 #include <arch_system_info.h> 12 #include <cpu.h> 13 #include <debug.h> 14 #include <smp.h> 15 #include <thread.h> 16 #include <util/AutoLock.h> 17 18 #include <cpuidle.h> 19 #include <KernelExport.h> 20 21 #include <stdio.h> 22 23 24 #define CPUIDLE_CSTATE_MAX 8 25 26 #define MWAIT_INTERRUPTS_BREAK (1 << 0) 27 28 #define X86_CSTATES_MODULE_NAME CPUIDLE_MODULES_PREFIX "/x86_cstates/v1" 29 30 #define BASE_TIME_STEP 500 31 32 struct CState { 33 uint32 fCode; 34 int fSubStatesCount; 35 }; 36 37 static CState sCStates[CPUIDLE_CSTATE_MAX]; 38 static int sCStateCount; 39 40 static int sTimeStep = BASE_TIME_STEP; 41 static bool sEnableWait = false; 42 43 static bigtime_t* sIdleTime; 44 45 46 static inline void 47 x86_monitor(void* address, uint32 ecx, uint32 edx) 48 { 49 asm volatile("monitor" : : "a" (address), "c" (ecx), "d"(edx)); 50 } 51 52 53 static inline void 54 x86_mwait(uint32 eax, uint32 ecx) 55 { 56 asm volatile("mwait" : : "a" (eax), "c" (ecx)); 57 } 58 59 60 static void 61 cstates_set_scheduler_mode(scheduler_mode mode) 62 { 63 if (mode == SCHEDULER_MODE_POWER_SAVING) { 64 sTimeStep = BASE_TIME_STEP / 4; 65 sEnableWait = true; 66 } else { 67 sTimeStep = BASE_TIME_STEP; 68 sEnableWait = false; 69 } 70 } 71 72 73 static void 74 cstates_idle(void) 75 { 76 ASSERT(thread_get_current_thread()->pinned_to_cpu > 0); 77 int32 cpu = smp_get_current_cpu(); 78 79 bigtime_t timeStep = sTimeStep; 80 bigtime_t idleTime = sIdleTime[cpu]; 81 int state = min_c(idleTime / timeStep, sCStateCount - 1); 82 83 if(state < 0 || state >= sCStateCount) { 84 panic("State %d of CPU %" B_PRId32 " is out of range (0 to %d), " 85 "idleTime %" B_PRIdBIGTIME "/%" B_PRIdBIGTIME, state, cpu, 86 sCStateCount, idleTime, timeStep); 87 } 88 89 int subState = idleTime % timeStep; 90 subState *= sCStates[state].fSubStatesCount; 91 subState /= timeStep; 92 93 ASSERT(subState >= 0 && subState < sCStates[state].fSubStatesCount); 94 95 InterruptsLocker locker; 96 int dummy; 97 bigtime_t start = system_time(); 98 x86_monitor(&dummy, 0, 0); 99 x86_mwait(sCStates[state].fCode | subState, MWAIT_INTERRUPTS_BREAK); 100 bigtime_t delta = system_time() - start; 101 locker.Unlock(); 102 103 // Negative delta shouldn't happen, but apparently it does... 104 if (delta >= 0) 105 sIdleTime[cpu] = (idleTime + delta) / 2; 106 } 107 108 109 static void 110 cstates_wait(int32* variable, int32 test) 111 { 112 if (!sEnableWait) { 113 arch_cpu_pause(); 114 return; 115 } 116 117 InterruptsLocker _; 118 x86_monitor(variable, 0, 0); 119 if (*variable != test) 120 x86_mwait(sCStates[0].fCode, MWAIT_INTERRUPTS_BREAK); 121 } 122 123 124 static status_t 125 init_cstates() 126 { 127 if (!x86_check_feature(IA32_FEATURE_EXT_MONITOR, FEATURE_EXT)) 128 return B_ERROR; 129 if (!x86_check_feature(IA32_FEATURE_POWER_MWAIT, FEATURE_5_ECX)) 130 return B_ERROR; 131 if (!x86_check_feature(IA32_FEATURE_INTERRUPT_MWAIT, FEATURE_5_ECX)) 132 return B_ERROR; 133 134 // we need invariant TSC 135 if (!x86_check_feature(IA32_FEATURE_INVARIANT_TSC, FEATURE_EXT_7_EDX)) 136 return B_ERROR; 137 138 // get C-state data 139 cpuid_info cpuid; 140 get_current_cpuid(&cpuid, 0, 0); 141 uint32 maxBasicLeaf = cpuid.eax_0.max_eax; 142 if (maxBasicLeaf < 5) 143 return B_ERROR; 144 145 get_current_cpuid(&cpuid, 5, 0); 146 if ((cpuid.regs.eax & 0xffff) < sizeof(int32)) 147 return B_ERROR; 148 149 char cStates[64]; 150 unsigned int offset = 0; 151 for (int32 i = 1; i < CPUIDLE_CSTATE_MAX; i++) { 152 int32 subStates = (cpuid.regs.edx >> (i * 4)) & 0xf; 153 // no sub-states means the state is not available 154 if (subStates == 0) 155 continue; 156 157 if (offset < sizeof(cStates)) { 158 offset += snprintf(cStates + offset, sizeof(cStates) - offset, 159 ", C%" B_PRId32, i); 160 } 161 162 sCStates[sCStateCount].fCode = sCStateCount * 0x10; 163 sCStates[sCStateCount].fSubStatesCount = subStates; 164 sCStateCount++; 165 } 166 167 if (sCStateCount == 0) 168 return B_ERROR; 169 170 sIdleTime = new(std::nothrow) bigtime_t[smp_get_num_cpus()]; 171 if (sIdleTime == NULL) 172 return B_NO_MEMORY; 173 memset(sIdleTime, 0, sizeof(bigtime_t) * smp_get_num_cpus()); 174 175 cstates_set_scheduler_mode(SCHEDULER_MODE_LOW_LATENCY); 176 177 dprintf("using Intel C-States: C0%s\n", cStates); 178 return B_OK; 179 } 180 181 182 static status_t 183 uninit_cstates() 184 { 185 delete[] sIdleTime; 186 return B_OK; 187 } 188 189 190 static status_t 191 std_ops(int32 op, ...) 192 { 193 switch (op) { 194 case B_MODULE_INIT: 195 return init_cstates(); 196 197 case B_MODULE_UNINIT: 198 uninit_cstates(); 199 return B_OK; 200 } 201 202 return B_ERROR; 203 } 204 205 206 static cpuidle_module_info sX86CStates = { 207 { 208 X86_CSTATES_MODULE_NAME, 209 0, 210 std_ops, 211 }, 212 213 0.8f, 214 215 cstates_set_scheduler_mode, 216 217 cstates_idle, 218 cstates_wait 219 }; 220 221 222 module_info* modules[] = { 223 (module_info*)&sX86CStates, 224 NULL 225 }; 226 227