cstate.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. /*++
  2. Copyright (c) 2015 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. cstate.c
  5. Abstract:
  6. This module implements generic kernel support for processor C-state
  7. transitions.
  8. Author:
  9. Evan Green 25-Sep-2015
  10. Environment:
  11. Kernel
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <minoca/kernel/kernel.h>
  17. #include "pmp.h"
  18. //
  19. // ---------------------------------------------------------------- Definitions
  20. //
  21. //
  22. // Define how many CPU idle history events to keep around, as a log2 (bit
  23. // shift) value.
  24. //
  25. #define PM_CSTATE_HISTORY_SHIFT 7
  26. //
  27. // ------------------------------------------------------ Data Type Definitions
  28. //
  29. /*++
  30. Structure Description:
  31. This structure defines statistics for C-state transitions on a specific
  32. processor.
  33. Members:
  34. EntryCount - Stores the number of times this state has been entered.
  35. TimeSpent - Stores the total number of time counter ticks spent in this
  36. state.
  37. --*/
  38. typedef struct _PM_CSTATE_STATISTICS {
  39. ULONGLONG EntryCount;
  40. ULONGLONG TimeSpent;
  41. } PM_CSTATE_STATISTICS, *PPM_CSTATE_STATISTICS;
  42. /*++
  43. Structure Description:
  44. This structure defines the internal idle state context for a particular
  45. processor.
  46. Members:
  47. Driver - Stores the driver information for this processor.
  48. History - Stores the processor's idle history.
  49. Statistics - Stores an array of statistics for each C-state. The first
  50. index is reserved for halt-only transitions. Starting with index one,
  51. each entry corresponds to a C-state described by the C-state driver.
  52. --*/
  53. typedef struct _PM_PROCESSOR_CSTATE_DATA {
  54. PM_IDLE_PROCESSOR_STATE Driver;
  55. PIDLE_HISTORY History;
  56. PPM_CSTATE_STATISTICS Statistics;
  57. } PM_PROCESSOR_CSTATE_DATA, *PPM_PROCESSOR_CSTATE_DATA;
  58. /*++
  59. Structure Description:
  60. This structure defines the global kernel idle state information.
  61. Members:
  62. Interface - Stores a pointer to the interface.
  63. Processors - Stores a pointer to the array of processor idle state
  64. structures.
  65. ProcessorCount - Stores the number of processors in the array.
  66. --*/
  67. typedef struct _PM_CSTATE_DATA {
  68. PPM_IDLE_STATE_INTERFACE Interface;
  69. PPM_PROCESSOR_CSTATE_DATA Processors;
  70. ULONG ProcessorCount;
  71. } PM_CSTATE_DATA, *PPM_CSTATE_DATA;
  72. /*++
  73. Structure Description:
  74. This structure defines the context used while the C-states are initializing
  75. on each processor.
  76. Members:
  77. Data - Stores a pointer to the interface data.
  78. FinishEvent - Stores a pointer to an event that will be signaled when the
  79. initialization is finished one way or another.
  80. Status - Stores the resulting status code of the initialization.
  81. --*/
  82. typedef struct _PM_CSTATE_INITIALIZATION_CONTEXT {
  83. PPM_CSTATE_DATA Data;
  84. PKEVENT Event;
  85. KSTATUS Status;
  86. } PM_CSTATE_INITIALIZATION_CONTEXT, *PPM_CSTATE_INITIALIZATION_CONTEXT;
  87. //
  88. // ----------------------------------------------- Internal Function Prototypes
  89. //
  90. KSTATUS
  91. PmpInitializeIdleStates (
  92. PPM_IDLE_STATE_INTERFACE Interface
  93. );
  94. VOID
  95. PmpInitializeProcessorIdleStatesDpc (
  96. PDPC Dpc
  97. );
  98. VOID
  99. PmpDebugPrintCstateStatistics (
  100. PPM_PROCESSOR_CSTATE_DATA Data
  101. );
  102. //
  103. // -------------------------------------------------------------------- Globals
  104. //
  105. //
  106. // Set this boolean to prevent entering any advanced C-states.
  107. //
  108. BOOL PmDisableCstates;
  109. //
  110. // Set this boolean to print all C-state information.
  111. //
  112. ULONG PmPrintCstateStatistics;
  113. //
  114. // Set this boolean to prevent halting at all when idle. The disable C-states
  115. // boolean also needs to be set to full disable any processor hardware idle
  116. // states.
  117. //
  118. BOOL PmDisableHalt;
  119. //
  120. // Store the global C-state context.
  121. //
  122. PPM_CSTATE_DATA PmCstateData;
  123. //
  124. // ------------------------------------------------------------------ Functions
  125. //
  126. VOID
  127. PmIdle (
  128. PPROCESSOR_BLOCK Processor
  129. )
  130. /*++
  131. Routine Description:
  132. This routine is called on a processor to go into a low power idle state. If
  133. no processor idle driver has been registered or processor idle states have
  134. been disabled, then the processor simply halts waiting for an interrupt.
  135. This routine is called with interrupts disabled and returns with interrupts
  136. enabled. This routine should only be called internally by the idle thread.
  137. Arguments:
  138. Processor - Supplies a pointer to the processor going idle.
  139. Return Value:
  140. None. This routine returns from the idle state with interrupts enabled.
  141. --*/
  142. {
  143. PPM_CSTATE_DATA Data;
  144. ULONGLONG Duration;
  145. ULONGLONG EndTime;
  146. ULONGLONG Estimate;
  147. ULONG Index;
  148. PPM_PROCESSOR_CSTATE_DATA ProcessorData;
  149. ULONGLONG StartTime;
  150. PPM_IDLE_STATE States;
  151. ULONG TargetState;
  152. Data = PmCstateData;
  153. //
  154. // If there is no C-state data, just do a halt.
  155. //
  156. if (Data == NULL) {
  157. if (PmDisableHalt != FALSE) {
  158. ArEnableInterrupts();
  159. } else {
  160. ArWaitForInterrupt();
  161. }
  162. return;
  163. }
  164. ProcessorData = &(Data->Processors[Processor->ProcessorNumber]);
  165. if (PmPrintCstateStatistics != 0) {
  166. if (RtlAtomicExchange32(&PmPrintCstateStatistics, 0) != 0) {
  167. for (Index = 0; Index < PmCstateData->ProcessorCount; Index += 1) {
  168. PmpDebugPrintCstateStatistics(
  169. &(PmCstateData->Processors[Index]));
  170. }
  171. }
  172. }
  173. //
  174. // Figure out an estimate for how long this processor will be idle by
  175. // looking at the average of its last few idle transitions.
  176. //
  177. Estimate = PmpIdleHistoryGetAverage(ProcessorData->History);
  178. //
  179. // Figure out the best state to go to, overshooting by one.
  180. //
  181. TargetState = 0;
  182. if (PmDisableCstates == FALSE) {
  183. States = ProcessorData->Driver.States;
  184. while ((TargetState < ProcessorData->Driver.StateCount) &&
  185. (Estimate >= States[TargetState].TargetResidency)) {
  186. TargetState += 1;
  187. }
  188. }
  189. //
  190. // Snap the start time, and go idle.
  191. //
  192. StartTime = HlQueryTimeCounter();
  193. if (TargetState == 0) {
  194. ProcessorData->Driver.CurrentState = PM_IDLE_STATE_HALT;
  195. ArWaitForInterrupt();
  196. ArDisableInterrupts();
  197. } else {
  198. //
  199. // The loop above overshot by one, so back it down, and go to the idle
  200. // state.
  201. //
  202. ProcessorData->Driver.CurrentState = TargetState - 1;
  203. Data->Interface->EnterIdleState(&(ProcessorData->Driver),
  204. TargetState - 1);
  205. //
  206. // The driver may have actually entered a different state.
  207. //
  208. TargetState = ProcessorData->Driver.CurrentState + 1;
  209. }
  210. EndTime = HlQueryTimeCounter();
  211. if (EndTime < StartTime) {
  212. RtlDebugPrint("CSTATE: Time went backwards from %I64x to %I64x\n",
  213. StartTime,
  214. EndTime);
  215. }
  216. //
  217. // Compute this last idle duration and add it as a historical datapoint.
  218. //
  219. Duration = EndTime - StartTime;
  220. ProcessorData->Driver.CurrentState = PM_IDLE_STATE_NONE;
  221. PmpIdleHistoryAddDataPoint(ProcessorData->History, Duration);
  222. //
  223. // Mark the statistics as well. The index is offset by one to make room for
  224. // the "halt-only" entry.
  225. //
  226. ProcessorData->Statistics[TargetState].EntryCount += 1;
  227. ProcessorData->Statistics[TargetState].TimeSpent += Duration;
  228. ArEnableInterrupts();
  229. return;
  230. }
  231. KSTATUS
  232. PmpGetSetIdleStateHandlers (
  233. BOOL FromKernelMode,
  234. PVOID Data,
  235. PUINTN DataSize,
  236. BOOL Set
  237. )
  238. /*++
  239. Routine Description:
  240. This routine gets or sets the idle state handlers. In this case the data
  241. pointer is used directly (so the interface structure must not disappear
  242. after the call). This can only be set, can only be set once, and can only
  243. be set from kernel mode for obvious reasons.
  244. Arguments:
  245. FromKernelMode - Supplies a boolean indicating whether or not this request
  246. (and the buffer associated with it) originates from user mode (FALSE)
  247. or kernel mode (TRUE).
  248. Data - Supplies a pointer to the data buffer where the data is either
  249. returned for a get operation or given for a set operation.
  250. DataSize - Supplies a pointer that on input contains the size of the
  251. data buffer. On output, contains the required size of the data buffer.
  252. Set - Supplies a boolean indicating if this is a get operation (FALSE) or
  253. a set operation (TRUE).
  254. Return Value:
  255. STATUS_SUCCESS if the performance state information was initialized.
  256. STATUS_NOT_SUPPORTED for a get operation.
  257. STATUS_PERMISSION_DENIED if this is a user mode request.
  258. STATUS_DATA_LENGTH_MISMATCH if the data size is not the size of the
  259. PM_IDLE_STATE_INTERFACE structure.
  260. STATUS_TOO_LATE if performance state handlers have already been registered.
  261. Other errors if the performance state runtime could not be initialized.
  262. --*/
  263. {
  264. if (FromKernelMode == FALSE) {
  265. return STATUS_PERMISSION_DENIED;
  266. }
  267. if (Set == FALSE) {
  268. return STATUS_NOT_SUPPORTED;
  269. }
  270. if (*DataSize != sizeof(PM_IDLE_STATE_INTERFACE)) {
  271. *DataSize = sizeof(PM_IDLE_STATE_INTERFACE);
  272. return STATUS_DATA_LENGTH_MISMATCH;
  273. }
  274. if (PmCstateData != NULL) {
  275. return STATUS_TOO_LATE;
  276. }
  277. return PmpInitializeIdleStates(Data);
  278. }
  279. //
  280. // --------------------------------------------------------- Internal Functions
  281. //
  282. KSTATUS
  283. PmpInitializeIdleStates (
  284. PPM_IDLE_STATE_INTERFACE Interface
  285. )
  286. /*++
  287. Routine Description:
  288. This routine initializes the CPU idle state interface.
  289. Arguments:
  290. Interface - Supplies a pointer to the idle state interface.
  291. Return Value:
  292. Status code.
  293. --*/
  294. {
  295. UINTN AllocationSize;
  296. PPM_CSTATE_DATA Data;
  297. PDPC Dpc;
  298. ULONG Index;
  299. PM_CSTATE_INITIALIZATION_CONTEXT InitializationContext;
  300. ULONG ProcessorCount;
  301. PPM_PROCESSOR_CSTATE_DATA ProcessorData;
  302. KSTATUS Status;
  303. ASSERT(PmCstateData == NULL);
  304. ASSERT(KeGetRunLevel() == RunLevelLow);
  305. RtlZeroMemory(&InitializationContext,
  306. sizeof(PM_CSTATE_INITIALIZATION_CONTEXT));
  307. Dpc = NULL;
  308. ProcessorCount = KeGetActiveProcessorCount();
  309. AllocationSize = sizeof(PM_CSTATE_DATA) +
  310. (ProcessorCount * sizeof(PM_PROCESSOR_CSTATE_DATA));
  311. Data = MmAllocateNonPagedPool(AllocationSize, PM_ALLOCATION_TAG);
  312. if (Data == NULL) {
  313. Status = STATUS_INSUFFICIENT_RESOURCES;
  314. goto InitializeIdleStatesEnd;
  315. }
  316. RtlZeroMemory(Data, AllocationSize);
  317. Data->Interface = Interface;
  318. Data->ProcessorCount = ProcessorCount;
  319. Data->Processors = (PPM_PROCESSOR_CSTATE_DATA)(Data + 1);
  320. for (Index = 0; Index < ProcessorCount; Index += 1) {
  321. ProcessorData = &(Data->Processors[Index]);
  322. ProcessorData->Driver.CurrentState = PM_IDLE_STATE_NONE;
  323. ProcessorData->History = PmpCreateIdleHistory(IDLE_HISTORY_NON_PAGED,
  324. PM_CSTATE_HISTORY_SHIFT);
  325. if (ProcessorData->History == NULL) {
  326. Status = STATUS_INSUFFICIENT_RESOURCES;
  327. goto InitializeIdleStatesEnd;
  328. }
  329. }
  330. Dpc = KeCreateDpc(PmpInitializeProcessorIdleStatesDpc,
  331. &InitializationContext);
  332. if (Dpc == NULL) {
  333. Status = STATUS_INSUFFICIENT_RESOURCES;
  334. goto InitializeIdleStatesEnd;
  335. }
  336. InitializationContext.Data = Data;
  337. InitializationContext.Event = KeCreateEvent(NULL);
  338. if (InitializationContext.Event == NULL) {
  339. Status = STATUS_INSUFFICIENT_RESOURCES;
  340. goto InitializeIdleStatesEnd;
  341. }
  342. KeSignalEvent(InitializationContext.Event, SignalOptionUnsignal);
  343. //
  344. // Queue the DPC on each processor successively, starting with 0, and wait
  345. // for it to finish.
  346. //
  347. KeQueueDpcOnProcessor(Dpc, 0);
  348. KeWaitForEvent(InitializationContext.Event, FALSE, WAIT_TIME_INDEFINITE);
  349. Status = InitializationContext.Status;
  350. InitializeIdleStatesEnd:
  351. if (InitializationContext.Event != NULL) {
  352. KeDestroyEvent(InitializationContext.Event);
  353. }
  354. if (Dpc != NULL) {
  355. KeDestroyDpc(Dpc);
  356. }
  357. if (!KSUCCESS(Status)) {
  358. if (Data != NULL) {
  359. for (Index = 0; Index < ProcessorCount; Index += 1) {
  360. ProcessorData = &(Data->Processors[Index]);
  361. if (ProcessorData->History != NULL) {
  362. PmpDestroyIdleHistory(ProcessorData->History);
  363. }
  364. if (ProcessorData->Statistics != NULL) {
  365. MmFreeNonPagedPool(ProcessorData->Statistics);
  366. }
  367. }
  368. MmFreeNonPagedPool(Data);
  369. Data = NULL;
  370. }
  371. }
  372. PmCstateData = Data;
  373. return Status;
  374. }
  375. VOID
  376. PmpInitializeProcessorIdleStatesDpc (
  377. PDPC Dpc
  378. )
  379. /*++
  380. Routine Description:
  381. This routine initializes the processor idle state information for a
  382. particular processor. It then queues itself on the next processor.
  383. Arguments:
  384. Dpc - Supplies a pointer to the DPC that is running.
  385. Return Value:
  386. None.
  387. --*/
  388. {
  389. UINTN AllocationSize;
  390. PPM_CSTATE_INITIALIZATION_CONTEXT Context;
  391. PPROCESSOR_BLOCK Processor;
  392. PPM_PROCESSOR_CSTATE_DATA ProcessorData;
  393. KSTATUS Status;
  394. Processor = KeGetCurrentProcessorBlock();
  395. Context = Dpc->UserData;
  396. ProcessorData = &(Context->Data->Processors[Processor->ProcessorNumber]);
  397. ProcessorData->Driver.ProcessorNumber = Processor->ProcessorNumber;
  398. Status = Context->Data->Interface->InitializeIdleStates(
  399. Context->Data->Interface,
  400. &(ProcessorData->Driver));
  401. if (!KSUCCESS(Status)) {
  402. goto InitializeProcessorIdleStatesDpcEnd;
  403. }
  404. AllocationSize = (ProcessorData->Driver.StateCount + 1) *
  405. sizeof(PM_CSTATE_STATISTICS);
  406. ProcessorData->Statistics = MmAllocateNonPagedPool(AllocationSize,
  407. PM_ALLOCATION_TAG);
  408. if (ProcessorData->Statistics == NULL) {
  409. Status = STATUS_INSUFFICIENT_RESOURCES;
  410. goto InitializeProcessorIdleStatesDpcEnd;
  411. }
  412. RtlZeroMemory(ProcessorData->Statistics, AllocationSize);
  413. Status = STATUS_SUCCESS;
  414. InitializeProcessorIdleStatesDpcEnd:
  415. //
  416. // If it failed or this was the last one, end now.
  417. //
  418. if ((!KSUCCESS(Status)) ||
  419. (Processor->ProcessorNumber == Context->Data->ProcessorCount - 1)) {
  420. Context->Status = Status;
  421. //
  422. // As soon as the event is signaled, none of these structures are safe
  423. // to touch anymore.
  424. //
  425. KeSignalEvent(Context->Event, SignalOptionSignalAll);
  426. //
  427. // This initialization was successful and this is not the last processor,
  428. // so queue this DPC on the next processor. Again, now the structures are
  429. // no longer safe to touch.
  430. //
  431. } else {
  432. KeQueueDpcOnProcessor(Dpc, Processor->ProcessorNumber + 1);
  433. }
  434. return;
  435. }
  436. VOID
  437. PmpDebugPrintCstateStatistics (
  438. PPM_PROCESSOR_CSTATE_DATA Data
  439. )
  440. /*++
  441. Routine Description:
  442. This routine prints C-state statistics for the given processor.
  443. Arguments:
  444. Data - Supplies a pointer to the processor data.
  445. Return Value:
  446. None.
  447. --*/
  448. {
  449. PSTR Ending;
  450. ULONGLONG ExitLatency;
  451. ULONGLONG Frequency;
  452. ULONG Index;
  453. PSTR Name;
  454. PPM_IDLE_STATE State;
  455. PPM_CSTATE_STATISTICS Statistics;
  456. ULONGLONG TargetResidency;
  457. ULONGLONG TimeSpent;
  458. ULONGLONG TotalEvents;
  459. Frequency = HlQueryTimeCounterFrequency();
  460. RtlDebugPrint("Processor %d C-States:\n"
  461. " Name Exit Target, Count Time\n",
  462. Data->Driver.ProcessorNumber);
  463. TotalEvents = 0;
  464. for (Index = 0; Index <= Data->Driver.StateCount; Index += 1) {
  465. if (Index == 0) {
  466. Name = "(halt)";
  467. ExitLatency = 0;
  468. TargetResidency = 0;
  469. } else {
  470. State = &(Data->Driver.States[Index - 1]);
  471. Name = State->Name;
  472. TargetResidency = (State->TargetResidency *
  473. MICROSECONDS_PER_SECOND) / Frequency;
  474. ExitLatency = (State->ExitLatency *
  475. MICROSECONDS_PER_SECOND) / Frequency;
  476. }
  477. Statistics = &(Data->Statistics[Index]);
  478. TimeSpent = (Statistics->TimeSpent * MICROSECONDS_PER_SECOND) /
  479. Frequency;
  480. Ending = "us";
  481. if (TimeSpent > 10 * MICROSECONDS_PER_SECOND) {
  482. TimeSpent = Statistics->TimeSpent / Frequency;
  483. Ending = "s";
  484. }
  485. RtlDebugPrint("%8s: %5I64d %6I64d, %8I64d %I64d %s\n",
  486. Name,
  487. ExitLatency,
  488. TargetResidency,
  489. Statistics->EntryCount,
  490. TimeSpent,
  491. Ending);
  492. TotalEvents += Statistics->EntryCount;
  493. }
  494. RtlDebugPrint("Total Events: %I64d\n", TotalEvents);
  495. return;
  496. }