sched.c 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445
  1. /*++
  2. Copyright (c) 2015 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. sched.c
  9. Abstract:
  10. This module implements the kernel thread scheduler.
  11. Author:
  12. Evan Green 7-Apr-2015
  13. Environment:
  14. Kernel
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/kernel/kernel.h>
  20. #include "kep.h"
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. //
  25. // Define the minimum number of ready threads on a scheduler before another
  26. // scheduler will consider stealing from it.
  27. //
  28. #define SCHEDULER_REBALANCE_MINIMUM_THREADS 2
  29. //
  30. // ------------------------------------------------------ Data Type Definitions
  31. //
  32. //
  33. // ----------------------------------------------- Internal Function Prototypes
  34. //
  35. VOID
  36. KepIdle (
  37. PPROCESSOR_BLOCK Processor
  38. );
  39. VOID
  40. KepBalanceIdleScheduler (
  41. VOID
  42. );
  43. BOOL
  44. KepEnqueueSchedulerEntry (
  45. PSCHEDULER_ENTRY Entry,
  46. BOOL LockHeld
  47. );
  48. VOID
  49. KepDequeueSchedulerEntry (
  50. PSCHEDULER_ENTRY Entry,
  51. BOOL LockHeld
  52. );
  53. PKTHREAD
  54. KepGetNextThread (
  55. PSCHEDULER_DATA Scheduler,
  56. BOOL SkipRunning
  57. );
  58. KSTATUS
  59. KepCreateSchedulerGroup (
  60. PSCHEDULER_GROUP *NewGroup
  61. );
  62. VOID
  63. KepDestroySchedulerGroup (
  64. PSCHEDULER_GROUP Group
  65. );
  66. VOID
  67. KepInitializeSchedulerGroupEntry (
  68. PSCHEDULER_GROUP_ENTRY GroupEntry,
  69. PSCHEDULER_DATA Scheduler,
  70. PSCHEDULER_GROUP Group,
  71. PSCHEDULER_GROUP_ENTRY ParentEntry
  72. );
  73. //
  74. // -------------------------------------------------------------------- Globals
  75. //
  76. SCHEDULER_GROUP KeRootSchedulerGroup;
  77. KSPIN_LOCK KeSchedulerGroupLock;
  78. //
  79. // Set this to TRUE to move a thread onto the current processor when unblocking
  80. // that thread.
  81. //
  82. BOOL KeSchedulerStealReadyThreads = FALSE;
  83. //
  84. // ------------------------------------------------------------------ Functions
  85. //
  86. KERNEL_API
  87. VOID
  88. KeYield (
  89. VOID
  90. )
  91. /*++
  92. Routine Description:
  93. This routine yields the current thread's execution. The thread remains in
  94. the ready state, and may not actually be scheduled out if no other threads
  95. are ready.
  96. Arguments:
  97. None.
  98. Return Value:
  99. None.
  100. --*/
  101. {
  102. RUNLEVEL OldRunLevel;
  103. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  104. KeSchedulerEntry(SchedulerReasonThreadYielding);
  105. KeLowerRunLevel(OldRunLevel);
  106. return;
  107. }
  108. VOID
  109. KeSchedulerEntry (
  110. SCHEDULER_REASON Reason
  111. )
  112. /*++
  113. Routine Description:
  114. This routine serves as the entry point to the thread scheduler. It may
  115. decide to schedule a new thread or simply return.
  116. Arguments:
  117. Reason - Supplies the scheduler with the reason why it's being called (ie
  118. run-level lowering, the thread is waiting, exiting, etc).
  119. Return Value:
  120. None.
  121. --*/
  122. {
  123. BOOL Enabled;
  124. BOOL FirstTime;
  125. PKTHREAD NextThread;
  126. PVOID NextThreadStack;
  127. THREAD_STATE NextThreadState;
  128. PKTHREAD OldThread;
  129. PPROCESSOR_BLOCK Processor;
  130. PVOID *SaveLocation;
  131. Enabled = FALSE;
  132. FirstTime = FALSE;
  133. Processor = KeGetCurrentProcessorBlock();
  134. //
  135. // The scheduler must be called at dispatch.
  136. //
  137. ASSERT(KeGetRunLevel() == RunLevelDispatch);
  138. ASSERT(ArAreInterruptsEnabled() != FALSE);
  139. //
  140. // It is illegal for a DPC routine to block.
  141. //
  142. if (Processor->DpcInProgress != NULL) {
  143. KeCrashSystem(CRASH_DPC_FAILURE,
  144. DpcCrashDpcBlocked,
  145. (UINTN)(Processor->DpcInProgress),
  146. 0,
  147. 0);
  148. }
  149. OldThread = Processor->RunningThread;
  150. KeAcquireSpinLock(&(Processor->Scheduler.Lock));
  151. //
  152. // Remove the old thread from the scheduler. Immediately put it back if
  153. // it's not blocking.
  154. //
  155. if (OldThread != Processor->IdleThread) {
  156. KepDequeueSchedulerEntry(&(OldThread->SchedulerEntry), TRUE);
  157. if ((Reason != SchedulerReasonThreadBlocking) &&
  158. (Reason != SchedulerReasonThreadSuspending) &&
  159. (Reason != SchedulerReasonThreadExiting)) {
  160. KepEnqueueSchedulerEntry(&(OldThread->SchedulerEntry), TRUE);
  161. }
  162. }
  163. //
  164. // Now that the old thread has accounted for its time, get the next thread
  165. // to run. This might be the old thread again.
  166. //
  167. NextThread = KepGetNextThread(&(Processor->Scheduler), FALSE);
  168. //
  169. // If there are no threads to run, run the idle thread.
  170. //
  171. if (NextThread == NULL) {
  172. NextThread = Processor->IdleThread;
  173. //
  174. // This had better not be the idle thread blocking.
  175. //
  176. ASSERT((OldThread != Processor->IdleThread) ||
  177. ((Reason != SchedulerReasonThreadBlocking) &&
  178. (Reason != SchedulerReasonThreadSuspending) &&
  179. (Reason != SchedulerReasonThreadExiting)));
  180. }
  181. //
  182. // Set the thread to running before releasing the scheduler lock to prevent
  183. // others from trying to steal this thread.
  184. //
  185. NextThreadState = NextThread->State;
  186. NextThread->State = ThreadStateRunning;
  187. KeReleaseSpinLock(&(Processor->Scheduler.Lock));
  188. //
  189. // Just return if there's no change.
  190. //
  191. if (OldThread == NextThread) {
  192. goto SchedulerEntryEnd;
  193. }
  194. //
  195. // Keep track of the old thread's behavior record.
  196. //
  197. if (Reason == SchedulerReasonDispatchInterrupt) {
  198. OldThread->ResourceUsage.Preemptions += 1;
  199. } else {
  200. OldThread->ResourceUsage.Yields += 1;
  201. }
  202. //
  203. // Profile this context switch if enabled.
  204. //
  205. SpCollectThreadStatistic(OldThread, Processor, Reason);
  206. ASSERT((NextThreadState == ThreadStateReady) ||
  207. (NextThreadState == ThreadStateFirstTime));
  208. //
  209. // Before setting the running thread to the new thread, charge the previous
  210. // time to the previous thread. If the next thread is a new user mode
  211. // thread, then start charging to usermode directly as the context swap
  212. // is going to jump there directly.
  213. //
  214. if (NextThreadState == ThreadStateFirstTime) {
  215. FirstTime = TRUE;
  216. if ((NextThread->Flags & THREAD_FLAG_USER_MODE) != 0) {
  217. KeBeginCycleAccounting(CycleAccountUser);
  218. } else {
  219. KeBeginCycleAccounting(CycleAccountKernel);
  220. }
  221. } else {
  222. KeBeginCycleAccounting(CycleAccountKernel);
  223. }
  224. KepArchPrepareForContextSwap(Processor, OldThread, NextThread);
  225. //
  226. // Disable interrupts and begin the transition to the new thread.
  227. //
  228. Enabled = ArDisableInterrupts();
  229. Processor->RunningThread = NextThread;
  230. Processor->PreviousThread = OldThread;
  231. //
  232. // Deal with reasons other than being preempted for scheduling the old
  233. // thread out.
  234. //
  235. switch (Reason) {
  236. //
  237. // If the scheduler wasn't invoked to block the thread, then it remains
  238. // ready to run. It must be set to ready only after it's been swapped out.
  239. //
  240. case SchedulerReasonDispatchInterrupt:
  241. case SchedulerReasonThreadYielding:
  242. break;
  243. //
  244. // The thread is waiting on an object. Let it be known that this thread is
  245. // on its way out (but isn't quite yet).
  246. //
  247. case SchedulerReasonThreadBlocking:
  248. ASSERT(OldThread != Processor->IdleThread);
  249. OldThread->State = ThreadStateBlocking;
  250. break;
  251. //
  252. // The thread is suspending, begin to take it down.
  253. //
  254. case SchedulerReasonThreadSuspending:
  255. ASSERT(OldThread != Processor->IdleThread);
  256. OldThread->State = ThreadStateSuspending;
  257. break;
  258. //
  259. // The thread is exiting. Set the state to exited and set this thread as
  260. // the previous thread to know to do something with it.
  261. //
  262. case SchedulerReasonThreadExiting:
  263. OldThread->State = ThreadStateExited;
  264. break;
  265. //
  266. // Unknown case!
  267. //
  268. default:
  269. ASSERT(FALSE);
  270. break;
  271. }
  272. //
  273. // Prepare threads running for the first time.
  274. //
  275. if (FirstTime != FALSE) {
  276. //
  277. // The thread should start at low level, and is jumped to immediately
  278. // by the context swap assembly. Interrupts will be enabled on the new
  279. // thread because of the return from exception mechanism.
  280. //
  281. KeLowerRunLevel(RunLevelLow);
  282. }
  283. SaveLocation = &(OldThread->KernelStackPointer);
  284. //
  285. // The thread is running, it shouldn't have a saved stack.
  286. //
  287. ASSERT(*SaveLocation == NULL);
  288. NextThreadStack = NextThread->KernelStackPointer;
  289. ASSERT(NextThreadStack >= KERNEL_VA_START);
  290. NextThread->KernelStackPointer = NULL;
  291. KepContextSwap(SaveLocation,
  292. NextThreadStack,
  293. NextThread->ThreadPointer,
  294. FirstTime);
  295. //
  296. // If this thread is being run again and had launched a new thread the last
  297. // time it was scheduled out, it will come back with interrupts disabled.
  298. // Re-enable them here.
  299. //
  300. if (Enabled != FALSE) {
  301. ArEnableInterrupts();
  302. }
  303. SchedulerEntryEnd:
  304. ASSERT(KeGetRunLevel() == RunLevelDispatch);
  305. ASSERT(ArAreInterruptsEnabled() != FALSE);
  306. return;
  307. }
  308. VOID
  309. KeSetThreadReady (
  310. PKTHREAD Thread
  311. )
  312. /*++
  313. Routine Description:
  314. This routine unblocks a previously blocked thread and adds it to the
  315. ready queue.
  316. Arguments:
  317. Thread - Supplies a pointer to the blocked thread.
  318. Return Value:
  319. None.
  320. --*/
  321. {
  322. BOOL FirstThread;
  323. PSCHEDULER_GROUP Group;
  324. PSCHEDULER_GROUP_ENTRY GroupEntry;
  325. PSCHEDULER_GROUP_ENTRY NewGroupEntry;
  326. RUNLEVEL OldRunLevel;
  327. PPROCESSOR_BLOCK ProcessorBlock;
  328. ASSERT((Thread->State == ThreadStateWaking) ||
  329. (Thread->State == ThreadStateFirstTime));
  330. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  331. GroupEntry = PARENT_STRUCTURE(Thread->SchedulerEntry.Parent,
  332. SCHEDULER_GROUP_ENTRY,
  333. Entry);
  334. if (Thread->State == ThreadStateFirstTime) {
  335. RtlAtomicAdd(&(GroupEntry->Group->ThreadCount), 1);
  336. } else {
  337. Thread->State = ThreadStateReady;
  338. }
  339. //
  340. // If the configuration option is set, steal the thread to run on the
  341. // current processor. This is bad for cache locality, but doesn't need an
  342. // IPI.
  343. //
  344. if (KeSchedulerStealReadyThreads != FALSE) {
  345. ProcessorBlock = KeGetCurrentProcessorBlock();
  346. Group = GroupEntry->Group;
  347. if (Group == &KeRootSchedulerGroup) {
  348. NewGroupEntry = &(ProcessorBlock->Scheduler.Group);
  349. } else {
  350. ASSERT(Group->EntryCount > ProcessorBlock->ProcessorNumber);
  351. NewGroupEntry = &(Group->Entries[ProcessorBlock->ProcessorNumber]);
  352. }
  353. Thread->SchedulerEntry.Parent = &(NewGroupEntry->Entry);
  354. KepEnqueueSchedulerEntry(&(Thread->SchedulerEntry), FALSE);
  355. //
  356. // Enqueue the thread on the processor it was previously on. This may
  357. // require waking that processor up.
  358. //
  359. } else {
  360. FirstThread = KepEnqueueSchedulerEntry(&(Thread->SchedulerEntry),
  361. FALSE);
  362. //
  363. // If this is the first thread being scheduled on the processor, then
  364. // make sure the clock is running (or wake it up).
  365. //
  366. if (FirstThread != FALSE) {
  367. ProcessorBlock = PARENT_STRUCTURE(GroupEntry->Scheduler,
  368. PROCESSOR_BLOCK,
  369. Scheduler);
  370. KepSetClockToPeriodic(ProcessorBlock);
  371. }
  372. }
  373. KeLowerRunLevel(OldRunLevel);
  374. return;
  375. }
  376. VOID
  377. KeSuspendExecution (
  378. VOID
  379. )
  380. /*++
  381. Routine Description:
  382. This routine suspends execution of the current thread until such time as
  383. another thread wakes it (usually because of a user mode signal).
  384. Arguments:
  385. None.
  386. Return Value:
  387. None. The function returns when another thread has woken this thread.
  388. --*/
  389. {
  390. RUNLEVEL OldRunLevel;
  391. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  392. KeSchedulerEntry(SchedulerReasonThreadSuspending);
  393. KeLowerRunLevel(OldRunLevel);
  394. return;
  395. }
  396. VOID
  397. KeUnlinkSchedulerEntry (
  398. PSCHEDULER_ENTRY Entry
  399. )
  400. /*++
  401. Routine Description:
  402. This routine unlinks a scheduler entry from its parent group.
  403. Arguments:
  404. Entry - Supplies a pointer to the entry to be unlinked.
  405. Return Value:
  406. None.
  407. --*/
  408. {
  409. PSCHEDULER_GROUP Group;
  410. PSCHEDULER_GROUP_ENTRY GroupEntry;
  411. UINTN Index;
  412. UINTN OldCount;
  413. PSCHEDULER_GROUP_ENTRY ParentGroupEntry;
  414. while (Entry->Parent != NULL) {
  415. ParentGroupEntry = PARENT_STRUCTURE(Entry->Parent,
  416. SCHEDULER_GROUP_ENTRY,
  417. Entry);
  418. if (Entry->Type == SchedulerEntryThread) {
  419. ASSERT(Entry->ListEntry.Next == NULL);
  420. OldCount = RtlAtomicAdd(&(ParentGroupEntry->Group->ThreadCount),
  421. -1);
  422. ASSERT((OldCount != 0) && (OldCount < 0x10000000));
  423. } else {
  424. GroupEntry = PARENT_STRUCTURE(Entry, SCHEDULER_GROUP_ENTRY, Entry);
  425. ASSERT(GroupEntry->Entry.Type == SchedulerEntryGroup);
  426. //
  427. // If the group entry became completely empty, check the other
  428. // entries too.
  429. //
  430. if ((GroupEntry->Group->ThreadCount == 0) &&
  431. (LIST_EMPTY(&(GroupEntry->Children)) != FALSE)) {
  432. Group = GroupEntry->Group;
  433. for (Index = 0; Index < Group->EntryCount; Group += 1) {
  434. GroupEntry = &(Group->Entries[Index]);
  435. if (LIST_EMPTY(&(GroupEntry->Children)) == FALSE) {
  436. break;
  437. }
  438. }
  439. //
  440. // If all the group's thread counts and children are zero,
  441. // destroy the group.
  442. //
  443. if (Index == Group->EntryCount) {
  444. KepDestroySchedulerGroup(Group);
  445. }
  446. }
  447. }
  448. Entry = &(ParentGroupEntry->Entry);
  449. }
  450. return;
  451. }
  452. VOID
  453. KeIdleLoop (
  454. VOID
  455. )
  456. /*++
  457. Routine Description:
  458. This routine executes the idle loop. It does not return. It can be
  459. executed only from the idle thread.
  460. Arguments:
  461. None.
  462. Return Value:
  463. None.
  464. --*/
  465. {
  466. BOOL Enabled;
  467. PPROCESSOR_BLOCK ProcessorBlock;
  468. ProcessorBlock = KeGetCurrentProcessorBlock();
  469. while (TRUE) {
  470. KeYield();
  471. if (ProcessorBlock->Scheduler.Group.ReadyThreadCount != 0) {
  472. continue;
  473. }
  474. KepBalanceIdleScheduler();
  475. //
  476. // Disable interrupts to commit to going down for idle. Without this
  477. // IPIs could come in and schedule new work after the ready thread
  478. // check but before halting.
  479. //
  480. Enabled = ArDisableInterrupts();
  481. ASSERT(Enabled != FALSE);
  482. //
  483. // After disabling interrupts, check to see if any threads snuck on
  484. // in the meantime, and abort the idle if so.
  485. //
  486. if (ProcessorBlock->Scheduler.Group.ReadyThreadCount != 0) {
  487. ArEnableInterrupts();
  488. continue;
  489. }
  490. KepIdle(ProcessorBlock);
  491. }
  492. return;
  493. }
  494. VOID
  495. KepInitializeScheduler (
  496. PPROCESSOR_BLOCK ProcessorBlock
  497. )
  498. /*++
  499. Routine Description:
  500. This routine initializes the scheduler for a processor.
  501. Arguments:
  502. ProcessorBlock - Supplies a pointer to the processor block.
  503. Return Value:
  504. None.
  505. --*/
  506. {
  507. KeInitializeSpinLock(&KeSchedulerGroupLock);
  508. INITIALIZE_LIST_HEAD(&(KeRootSchedulerGroup.Children));
  509. KeInitializeSpinLock(&(ProcessorBlock->Scheduler.Lock));
  510. KepInitializeSchedulerGroupEntry(&(ProcessorBlock->Scheduler.Group),
  511. &(ProcessorBlock->Scheduler),
  512. &KeRootSchedulerGroup,
  513. NULL);
  514. return;
  515. }
  516. //
  517. // --------------------------------------------------------- Internal Functions
  518. //
  519. VOID
  520. KepIdle (
  521. PPROCESSOR_BLOCK Processor
  522. )
  523. /*++
  524. Routine Description:
  525. This routine is called when a processor has nothing to run. This routine is
  526. called with interrupts disabled, and returns with interrupts enabled.
  527. Arguments:
  528. Processor - Supplies a pointer to the processor block of the current
  529. processor.
  530. Return Value:
  531. None.
  532. --*/
  533. {
  534. KepClockIdle(Processor);
  535. //
  536. // Begin counting this time as idle time. There's no need to put it back
  537. // to its previous setting at the end because the next thing this thread
  538. // will do is yield, and the scheduler will set the new period.
  539. //
  540. KeBeginCycleAccounting(CycleAccountIdle);
  541. PmIdle(Processor);
  542. return;
  543. }
  544. VOID
  545. KepBalanceIdleScheduler (
  546. VOID
  547. )
  548. /*++
  549. Routine Description:
  550. This routine is called when the processor is idle. It tries to steal
  551. threads from a busier processor.
  552. Arguments:
  553. None.
  554. Return Value:
  555. None.
  556. --*/
  557. {
  558. ULONG ActiveCount;
  559. ULONG CurrentNumber;
  560. PSCHEDULER_GROUP_ENTRY DestinationGroupEntry;
  561. BOOL FirstThread;
  562. PSCHEDULER_GROUP Group;
  563. ULONG Number;
  564. RUNLEVEL OldRunLevel;
  565. PPROCESSOR_BLOCK ProcessorBlock;
  566. PSCHEDULER_GROUP_ENTRY SourceGroupEntry;
  567. PSCHEDULER_DATA VictimScheduler;
  568. PKTHREAD VictimThread;
  569. ActiveCount = KeGetActiveProcessorCount();
  570. if (ActiveCount == 1) {
  571. return;
  572. }
  573. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  574. ASSERT(OldRunLevel == RunLevelLow);
  575. CurrentNumber = KeGetCurrentProcessorNumber();
  576. VictimThread = NULL;
  577. //
  578. // Try to steal from another processor, starting with the next neighbor.
  579. //
  580. Number = CurrentNumber + 1;
  581. while (TRUE) {
  582. if (Number == ActiveCount) {
  583. Number = 0;
  584. }
  585. if (Number == CurrentNumber) {
  586. break;
  587. }
  588. ProcessorBlock = KeProcessorBlocks[Number];
  589. VictimScheduler = &(ProcessorBlock->Scheduler);
  590. if (VictimScheduler->Group.ReadyThreadCount >=
  591. SCHEDULER_REBALANCE_MINIMUM_THREADS) {
  592. KeAcquireSpinLock(&(VictimScheduler->Lock));
  593. VictimThread = KepGetNextThread(VictimScheduler, TRUE);
  594. if (VictimThread != NULL) {
  595. ASSERT((VictimThread->State == ThreadStateReady) ||
  596. (VictimThread->State == ThreadStateFirstTime));
  597. //
  598. // Pull the thread out of the ready queue.
  599. //
  600. KepDequeueSchedulerEntry(&(VictimThread->SchedulerEntry), TRUE);
  601. }
  602. KeReleaseSpinLock(&(VictimScheduler->Lock));
  603. if (VictimThread != NULL) {
  604. //
  605. // Move the entry to this processor's queue.
  606. //
  607. SourceGroupEntry = PARENT_STRUCTURE(
  608. VictimThread->SchedulerEntry.Parent,
  609. SCHEDULER_GROUP_ENTRY,
  610. Entry);
  611. Group = SourceGroupEntry->Group;
  612. if (Group == &KeRootSchedulerGroup) {
  613. DestinationGroupEntry =
  614. &(KeProcessorBlocks[CurrentNumber]->Scheduler.Group);
  615. } else {
  616. ASSERT(Group->EntryCount > CurrentNumber);
  617. DestinationGroupEntry = &(Group->Entries[CurrentNumber]);
  618. }
  619. VictimThread->SchedulerEntry.Parent =
  620. &(DestinationGroupEntry->Entry);
  621. //
  622. // Enqueue the thread on this processor.
  623. //
  624. FirstThread =
  625. KepEnqueueSchedulerEntry(&(VictimThread->SchedulerEntry),
  626. FALSE);
  627. if (FirstThread != FALSE) {
  628. KepSetClockToPeriodic(KeProcessorBlocks[CurrentNumber]);
  629. }
  630. break;
  631. }
  632. }
  633. Number += 1;
  634. }
  635. KeLowerRunLevel(OldRunLevel);
  636. return;
  637. }
  638. BOOL
  639. KepEnqueueSchedulerEntry (
  640. PSCHEDULER_ENTRY Entry,
  641. BOOL LockHeld
  642. )
  643. /*++
  644. Routine Description:
  645. This routine adds the given entry to the active scheduler. This routine
  646. assumes the current runlevel is at dispatch, or interrupts are disabled.
  647. Arguments:
  648. Entry - Supplies a pointer to the entry to add.
  649. LockHeld - Supplies a boolean indicating whether or not the caller has the
  650. scheduler lock already held.
  651. Return Value:
  652. TRUE if this was the first thread scheduled on the top level group. This
  653. may indicate to callers that the processor may be out and idle.
  654. FALSE if this was not the first thread scheduled, or the entry being
  655. scheduled was not a thread.
  656. --*/
  657. {
  658. BOOL FirstThread;
  659. PSCHEDULER_GROUP_ENTRY GroupEntry;
  660. PSCHEDULER_DATA Scheduler;
  661. ASSERT((KeGetRunLevel() == RunLevelDispatch) ||
  662. (ArAreInterruptsEnabled() == FALSE));
  663. FirstThread = FALSE;
  664. if (LockHeld != FALSE) {
  665. GroupEntry = PARENT_STRUCTURE(Entry->Parent,
  666. SCHEDULER_GROUP_ENTRY,
  667. Entry);
  668. Scheduler = GroupEntry->Scheduler;
  669. } else {
  670. //
  671. // Chase the entity around as it bounces from group entry to group
  672. // entry.
  673. //
  674. while (TRUE) {
  675. GroupEntry = PARENT_STRUCTURE(Entry->Parent,
  676. SCHEDULER_GROUP_ENTRY,
  677. Entry);
  678. Scheduler = GroupEntry->Scheduler;
  679. KeAcquireSpinLock(&(Scheduler->Lock));
  680. if (Entry->Parent == &(GroupEntry->Entry)) {
  681. break;
  682. }
  683. KeReleaseSpinLock(&(Scheduler->Lock));
  684. }
  685. }
  686. //
  687. // Add the entry to the list.
  688. //
  689. ASSERT(Entry->ListEntry.Next == NULL);
  690. INSERT_BEFORE(&(Entry->ListEntry), &(GroupEntry->Children));
  691. //
  692. // Propagate the ready thread up through all levels.
  693. //
  694. if (Entry->Type == SchedulerEntryThread) {
  695. while (TRUE) {
  696. GroupEntry->ReadyThreadCount += 1;
  697. if (GroupEntry->Entry.Parent == NULL) {
  698. //
  699. // Remember if this is the first thread to become ready on the
  700. // top level group.
  701. //
  702. if (GroupEntry->ReadyThreadCount == 1) {
  703. FirstThread = TRUE;
  704. }
  705. break;
  706. }
  707. GroupEntry = PARENT_STRUCTURE(GroupEntry->Entry.Parent,
  708. SCHEDULER_GROUP_ENTRY,
  709. Entry);
  710. }
  711. }
  712. if (LockHeld == FALSE) {
  713. KeReleaseSpinLock(&(Scheduler->Lock));
  714. }
  715. return FirstThread;
  716. }
  717. VOID
  718. KepDequeueSchedulerEntry (
  719. PSCHEDULER_ENTRY Entry,
  720. BOOL LockHeld
  721. )
  722. /*++
  723. Routine Description:
  724. This routine removes the given entry to the active scheduler. This routine
  725. assumes the current runlevel is at dispatch, or interrupts are disabled.
  726. Arguments:
  727. Entry - Supplies a pointer to the entry to remove.
  728. LockHeld - Supplies a boolean indicating whether or not the caller has the
  729. scheduler lock already held.
  730. Return Value:
  731. None.
  732. --*/
  733. {
  734. PSCHEDULER_GROUP_ENTRY GroupEntry;
  735. PSCHEDULER_GROUP_ENTRY ParentGroupEntry;
  736. PSCHEDULER_DATA Scheduler;
  737. ASSERT((KeGetRunLevel() == RunLevelDispatch) ||
  738. (ArAreInterruptsEnabled() == FALSE));
  739. if (LockHeld != FALSE) {
  740. GroupEntry = PARENT_STRUCTURE(Entry->Parent,
  741. SCHEDULER_GROUP_ENTRY,
  742. Entry);
  743. Scheduler = GroupEntry->Scheduler;
  744. } else {
  745. //
  746. // Chase the entity around as it bounces from group entry to group
  747. // entry.
  748. //
  749. while (TRUE) {
  750. GroupEntry = PARENT_STRUCTURE(Entry->Parent,
  751. SCHEDULER_GROUP_ENTRY,
  752. Entry);
  753. Scheduler = GroupEntry->Scheduler;
  754. KeAcquireSpinLock(&(Scheduler->Lock));
  755. if (Entry->Parent == &(GroupEntry->Entry)) {
  756. break;
  757. }
  758. KeReleaseSpinLock(&(Scheduler->Lock));
  759. }
  760. }
  761. //
  762. // Remove the entry from the list.
  763. //
  764. ASSERT(Entry->ListEntry.Next != NULL);
  765. LIST_REMOVE(&(Entry->ListEntry));
  766. Entry->ListEntry.Next = NULL;
  767. //
  768. // Propagate the no-longer-ready thread up through all levels.
  769. //
  770. if (Entry->Type == SchedulerEntryThread) {
  771. while (TRUE) {
  772. GroupEntry->ReadyThreadCount -= 1;
  773. if (GroupEntry->Entry.Parent == NULL) {
  774. break;
  775. }
  776. ParentGroupEntry = PARENT_STRUCTURE(GroupEntry->Entry.Parent,
  777. SCHEDULER_GROUP_ENTRY,
  778. Entry);
  779. //
  780. // Rotate the groups so others at higher levels get a chance to run.
  781. //
  782. LIST_REMOVE(&(GroupEntry->Entry.ListEntry));
  783. INSERT_BEFORE(&(GroupEntry->Entry.ListEntry),
  784. &(ParentGroupEntry->Children));
  785. GroupEntry = ParentGroupEntry;
  786. }
  787. } else {
  788. GroupEntry = PARENT_STRUCTURE(Entry, SCHEDULER_GROUP_ENTRY, Entry);
  789. ASSERT(GroupEntry->ReadyThreadCount == 0);
  790. }
  791. if (LockHeld == FALSE) {
  792. KeReleaseSpinLock(&(Scheduler->Lock));
  793. }
  794. return;
  795. }
  796. PKTHREAD
  797. KepGetNextThread (
  798. PSCHEDULER_DATA Scheduler,
  799. BOOL SkipRunning
  800. )
  801. /*++
  802. Routine Description:
  803. This routine returns the next thread to run in the scheduler. This routine
  804. assumes the scheduler lock is already held.
  805. Arguments:
  806. Scheduler - Supplies a pointer to the scheduler to work on.
  807. SkipRunning - Supplies a boolean indicating whether to ignore the first
  808. thread on the queue if it's marked as running. This is used when trying
  809. to steal threads from another scheduler.
  810. Return Value:
  811. Returns a pointer to the next thread to run.
  812. NULL if no threads are ready to run.
  813. --*/
  814. {
  815. PSCHEDULER_GROUP_ENTRY ChildGroupEntry;
  816. PLIST_ENTRY CurrentEntry;
  817. PSCHEDULER_ENTRY Entry;
  818. PSCHEDULER_GROUP_ENTRY GroupEntry;
  819. PKTHREAD Thread;
  820. GroupEntry = &(Scheduler->Group);
  821. if (GroupEntry->ReadyThreadCount == 0) {
  822. return NULL;
  823. }
  824. CurrentEntry = GroupEntry->Children.Next;
  825. while (CurrentEntry != &(GroupEntry->Children)) {
  826. //
  827. // Get the next child of the group. If it's a thread, return it.
  828. //
  829. Entry = LIST_VALUE(CurrentEntry, SCHEDULER_ENTRY, ListEntry);
  830. if (Entry->Type == SchedulerEntryThread) {
  831. Thread = PARENT_STRUCTURE(Entry, KTHREAD, SchedulerEntry);
  832. if ((SkipRunning == FALSE) ||
  833. (Thread->State != ThreadStateRunning)) {
  834. return Thread;
  835. }
  836. //
  837. // This thread was not acceptable. Try to continue to the next
  838. // entry in the list, or pop back up to the parent group.
  839. //
  840. while (TRUE) {
  841. if (CurrentEntry->Next != &(GroupEntry->Children)) {
  842. CurrentEntry = CurrentEntry->Next;
  843. break;
  844. }
  845. if (GroupEntry->Entry.Parent == NULL) {
  846. CurrentEntry = CurrentEntry->Next;
  847. break;
  848. }
  849. CurrentEntry = &(GroupEntry->Entry.ListEntry);
  850. GroupEntry = PARENT_STRUCTURE(GroupEntry->Entry.Parent,
  851. SCHEDULER_GROUP_ENTRY,
  852. Entry);
  853. }
  854. continue;
  855. }
  856. //
  857. // The child is a group. If it has no children, continue to the sibling.
  858. //
  859. ASSERT(Entry->Type == SchedulerEntryGroup);
  860. ChildGroupEntry = PARENT_STRUCTURE(Entry, SCHEDULER_GROUP_ENTRY, Entry);
  861. if (ChildGroupEntry->ReadyThreadCount == 0) {
  862. CurrentEntry = CurrentEntry->Next;
  863. //
  864. // The child group has ready threads somewhere down there. Descend into
  865. // it.
  866. //
  867. } else {
  868. GroupEntry = ChildGroupEntry;
  869. CurrentEntry = GroupEntry->Children.Next;
  870. }
  871. }
  872. //
  873. // The end of the group was hit without finding a thread.
  874. //
  875. return NULL;
  876. }
  877. KSTATUS
  878. KepCreateSchedulerGroup (
  879. PSCHEDULER_GROUP *NewGroup
  880. )
  881. /*++
  882. Routine Description:
  883. This routine creates a new scheduler group underneath the current thread's
  884. scheduler group.
  885. Arguments:
  886. NewGroup - Supplies a pointer where a pointer to the new group will be
  887. returned on success.
  888. Return Value:
  889. Status code.
  890. --*/
  891. {
  892. UINTN AllocationSize;
  893. UINTN EntryCount;
  894. PSCHEDULER_GROUP Group;
  895. UINTN Index;
  896. RUNLEVEL OldRunLevel;
  897. PSCHEDULER_GROUP ParentGroup;
  898. PSCHEDULER_GROUP_ENTRY ParentGroupEntry;
  899. PKTHREAD Thread;
  900. //
  901. // Get the current thread's group, which serves as this new group's parent.
  902. //
  903. Thread = KeGetCurrentThread();
  904. ParentGroupEntry = PARENT_STRUCTURE(Thread->SchedulerEntry.Parent,
  905. SCHEDULER_GROUP_ENTRY,
  906. Entry);
  907. ASSERT(ParentGroupEntry->Entry.Type == SchedulerEntryGroup);
  908. ParentGroup = ParentGroupEntry->Group;
  909. //
  910. // Determine the number of entries in this group, which is capped by the
  911. // parent group.
  912. //
  913. EntryCount = KeGetActiveProcessorCount();
  914. if ((ParentGroup != &KeRootSchedulerGroup) &&
  915. (EntryCount > ParentGroup->EntryCount)) {
  916. EntryCount = ParentGroup->EntryCount;
  917. }
  918. AllocationSize = sizeof(SCHEDULER_GROUP) +
  919. (EntryCount * sizeof(SCHEDULER_GROUP_ENTRY));
  920. Group = MmAllocateNonPagedPool(AllocationSize, KE_SCHEDULER_ALLOCATION_TAG);
  921. if (Group == NULL) {
  922. return STATUS_INSUFFICIENT_RESOURCES;
  923. }
  924. RtlZeroMemory(Group, AllocationSize);
  925. INITIALIZE_LIST_HEAD(&(Group->Children));
  926. Group->Entries = (PSCHEDULER_GROUP_ENTRY)(Group + 1);
  927. Group->EntryCount = EntryCount;
  928. Group->Parent = ParentGroup;
  929. //
  930. // Add the group to the global tree.
  931. //
  932. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  933. KeAcquireSpinLock(&KeSchedulerGroupLock);
  934. INSERT_BEFORE(&(Group->ListEntry), &(ParentGroup->Children));
  935. KeReleaseSpinLock(&KeSchedulerGroupLock);
  936. KeLowerRunLevel(OldRunLevel);
  937. for (Index = 0; Index < EntryCount; Index += 1) {
  938. //
  939. // If the parent is the root, then schedule directly onto the processor.
  940. //
  941. if (ParentGroup == &KeRootSchedulerGroup) {
  942. ParentGroupEntry = &(KeProcessorBlocks[Index]->Scheduler.Group);
  943. } else {
  944. ParentGroupEntry = &(ParentGroup->Entries[Index]);
  945. }
  946. KepInitializeSchedulerGroupEntry(&(Group->Entries[Index]),
  947. &(KeProcessorBlocks[Index]->Scheduler),
  948. Group,
  949. ParentGroupEntry);
  950. //
  951. // Add the scheduler group entry to the parent scheduler group entry.
  952. //
  953. KepEnqueueSchedulerEntry(&(Group->Entries[Index].Entry), FALSE);
  954. }
  955. *NewGroup = Group;
  956. return STATUS_SUCCESS;
  957. }
  958. VOID
  959. KepDestroySchedulerGroup (
  960. PSCHEDULER_GROUP Group
  961. )
  962. /*++
  963. Routine Description:
  964. This routine unlinks and destroys a scheduler group.
  965. Arguments:
  966. Group - Supplies a pointer to the group to destroy.
  967. Return Value:
  968. None.
  969. --*/
  970. {
  971. PSCHEDULER_GROUP_ENTRY GroupEntry;
  972. UINTN Index;
  973. RUNLEVEL OldRunLevel;
  974. ASSERT(Group != &KeRootSchedulerGroup);
  975. ASSERT(Group->ThreadCount == 0);
  976. for (Index = 0; Index < Group->EntryCount; Index += 1) {
  977. GroupEntry = &(Group->Entries[Index]);
  978. ASSERT((GroupEntry->ReadyThreadCount == 0) &&
  979. (LIST_EMPTY(&(GroupEntry->Children)) != FALSE));
  980. KepDequeueSchedulerEntry(&(GroupEntry->Entry), FALSE);
  981. }
  982. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  983. KeAcquireSpinLock(&KeSchedulerGroupLock);
  984. LIST_REMOVE(&(Group->ListEntry));
  985. Group->ListEntry.Next = NULL;
  986. KeReleaseSpinLock(&KeSchedulerGroupLock);
  987. KeLowerRunLevel(OldRunLevel);
  988. MmFreeNonPagedPool(Group);
  989. return;
  990. }
  991. VOID
  992. KepInitializeSchedulerGroupEntry (
  993. PSCHEDULER_GROUP_ENTRY GroupEntry,
  994. PSCHEDULER_DATA Scheduler,
  995. PSCHEDULER_GROUP Group,
  996. PSCHEDULER_GROUP_ENTRY ParentEntry
  997. )
  998. /*++
  999. Routine Description:
  1000. This routine initializes a scheduler group entry structure.
  1001. Arguments:
  1002. GroupEntry - Supplies a pointer to the group entry to initialize.
  1003. Scheduler - Supplies a pointer to the scheduler that the group entry is on.
  1004. Group - Supplies the scheduler group that owns the group entry.
  1005. ParentEntry - Supplies a pointer to the parent group entry.
  1006. Return Value:
  1007. None.
  1008. --*/
  1009. {
  1010. GroupEntry->Entry.Type = SchedulerEntryGroup;
  1011. if (ParentEntry == NULL) {
  1012. GroupEntry->Entry.Parent = NULL;
  1013. } else {
  1014. GroupEntry->Entry.Parent = &(ParentEntry->Entry);
  1015. }
  1016. INITIALIZE_LIST_HEAD(&(GroupEntry->Children));
  1017. GroupEntry->ReadyThreadCount = 0;
  1018. GroupEntry->Group = Group;
  1019. GroupEntry->Scheduler = Scheduler;
  1020. return;
  1021. }