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
x86_monitor(void * address,uint32 ecx,uint32 edx)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
x86_mwait(uint32 eax,uint32 ecx)54 x86_mwait(uint32 eax, uint32 ecx)
55 {
56 asm volatile("mwait" : : "a" (eax), "c" (ecx));
57 }
58
59
60 static void
cstates_set_scheduler_mode(scheduler_mode mode)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
cstates_idle(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
cstates_wait(int32 * variable,int32 test)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
init_cstates()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
uninit_cstates()183 uninit_cstates()
184 {
185 delete[] sIdleTime;
186 return B_OK;
187 }
188
189
190 static status_t
std_ops(int32 op,...)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