xref: /reactos/ntoskrnl/ke/balmgr.c (revision 84ccccab)
1 /*
2 * PROJECT:         ReactOS Kernel
3 * LICENSE:         GPL - See COPYING in the top level directory
4 * FILE:            ntoskrnl/ke/balmgr.c
5 * PURPOSE:         Balance Set Manager
6 * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7 */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* GLOBALS *******************************************************************/
16 
17 #define THREAD_BOOST_PRIORITY (LOW_REALTIME_PRIORITY - 1)
18 ULONG KiReadyScanLast;
19 
20 /* PRIVATE FUNCTIONS *********************************************************/
21 
22 VOID
23 NTAPI
24 KiScanReadyQueues(IN PKDPC Dpc,
25                   IN PVOID DeferredContext,
26                   IN PVOID SystemArgument1,
27                   IN PVOID SystemArgument2)
28 {
29     PULONG ScanLast = DeferredContext;
30     ULONG ScanIndex = *ScanLast;
31     ULONG Count = 10, Number = 16;
32     PKPRCB Prcb = KiProcessorBlock[ScanIndex];
33     ULONG Index = Prcb->QueueIndex;
34     ULONG WaitLimit = KeTickCount.LowPart - 300;
35     ULONG Summary;
36     KIRQL OldIrql;
37     PLIST_ENTRY ListHead, NextEntry;
38     PKTHREAD Thread;
39 
40     /* Lock the dispatcher and PRCB */
41     OldIrql = KiAcquireDispatcherLock();
42     KiAcquirePrcbLock(Prcb);
43     /* Check if there's any thread that need help */
44     Summary = Prcb->ReadySummary & ((1 << THREAD_BOOST_PRIORITY) - 2);
45     if (Summary)
46     {
47         /* Start scan loop */
48         do
49         {
50             /* Normalize the index */
51             if (Index > (THREAD_BOOST_PRIORITY - 1)) Index = 1;
52 
53             /* Loop for ready threads */
54             if (Summary & PRIORITY_MASK(Index))
55             {
56                 /* Sanity check */
57                 ASSERT(!IsListEmpty(&Prcb->DispatcherReadyListHead[Index]));
58 
59                 /* Update summary and select list */
60                 Summary ^= PRIORITY_MASK(Index);
61                 ListHead = &Prcb->DispatcherReadyListHead[Index];
62                 NextEntry = ListHead->Flink;
63                 do
64                 {
65                     /* Select a thread */
66                     Thread = CONTAINING_RECORD(NextEntry,
67                                                KTHREAD,
68                                                WaitListEntry);
69                     ASSERT(Thread->Priority == Index);
70 
71                     /* Check if the thread has been waiting too long */
72                     if (WaitLimit >= Thread->WaitTime)
73                     {
74                         /* Remove the thread from the queue */
75                         NextEntry = NextEntry->Blink;
76                         ASSERT((Prcb->ReadySummary & PRIORITY_MASK(Index)));
77                         if (RemoveEntryList(NextEntry->Flink))
78                         {
79                             /* The list is empty now */
80                             Prcb->ReadySummary ^= PRIORITY_MASK(Index);
81                         }
82 
83                         /* Verify priority decrement and set the new one */
84                         ASSERT((Thread->PriorityDecrement >= 0) &&
85                                (Thread->PriorityDecrement <=
86                                 Thread->Priority));
87                         Thread->PriorityDecrement += (THREAD_BOOST_PRIORITY -
88                                                       Thread->Priority);
89                         ASSERT((Thread->PriorityDecrement >= 0) &&
90                                (Thread->PriorityDecrement <=
91                                 THREAD_BOOST_PRIORITY));
92 
93                         /* Update priority and insert into ready list */
94                         Thread->Priority = THREAD_BOOST_PRIORITY;
95                         Thread->Quantum = WAIT_QUANTUM_DECREMENT * 4;
96                         KiInsertDeferredReadyList(Thread);
97                         Count --;
98                     }
99 
100                     /* Go to the next entry */
101                     NextEntry = NextEntry->Flink;
102                     Number--;
103                 } while((NextEntry != ListHead) && (Number) && (Count));
104             }
105 
106             /* Increase index */
107             Index++;
108         } while ((Summary) && (Number) && (Count));
109     }
110 
111     /* Release the locks and dispatcher */
112     KiReleasePrcbLock(Prcb);
113     KiReleaseDispatcherLock(OldIrql);
114 
115     /* Update the queue index for next time */
116     if ((Count) && (Number))
117     {
118         /* Reset the queue at index 1 */
119         Prcb->QueueIndex = 1;
120     }
121     else
122     {
123         /* Set the index we're in now */
124         Prcb->QueueIndex = Index;
125     }
126 
127     /* Increment the CPU number for next time and normalize to CPU count */
128     ScanIndex++;
129     if (ScanIndex == KeNumberProcessors) ScanIndex = 0;
130 
131     /* Return the index */
132     *ScanLast = ScanIndex;
133 }
134 
135 VOID
136 NTAPI
137 KeBalanceSetManager(IN PVOID Context)
138 {
139     KDPC ScanDpc;
140     KTIMER PeriodTimer;
141     LARGE_INTEGER DueTime;
142     KWAIT_BLOCK WaitBlockArray[1];
143     PVOID WaitObjects[1];
144     NTSTATUS Status;
145 
146     /* Set us at a low real-time priority level */
147     KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
148 
149     /* Setup the timer and scanner DPC */
150     KeInitializeTimerEx(&PeriodTimer, SynchronizationTimer);
151     KeInitializeDpc(&ScanDpc, KiScanReadyQueues, &KiReadyScanLast);
152 
153     /* Setup the periodic timer */
154     DueTime.QuadPart = -1 * 10 * 1000 * 1000;
155     KeSetTimerEx(&PeriodTimer, DueTime, 1000, &ScanDpc);
156 
157     /* Setup the wait objects */
158     WaitObjects[0] = &PeriodTimer;
159     //WaitObjects[1] = MmWorkingSetManagerEvent; // NO WS Management Yet!
160 
161     /* Start wait loop */
162     do
163     {
164         /* Wait on our objects */
165         Status = KeWaitForMultipleObjects(1,
166                                           WaitObjects,
167                                           WaitAny,
168                                           Executive,
169                                           KernelMode,
170                                           FALSE,
171                                           NULL,
172                                           WaitBlockArray);
173         switch (Status)
174         {
175             /* Check if our timer expired */
176             case STATUS_WAIT_0:
177 
178                 /* Adjust lookaside lists */
179                 //ExAdjustLookasideDepth();
180 
181                 /* Call the working set manager */
182                 //MmWorkingSetManager();
183 
184                 /* FIXME: Outswap stacks */
185 
186                 /* Done */
187                 break;
188 
189             /* Check if the working set manager notified us */
190             case STATUS_WAIT_1:
191 
192                 /* Call the working set manager */
193                 //MmWorkingSetManager();
194                 break;
195 
196             /* Anything else */
197             default:
198                 DPRINT1("BALMGR: Illegal wait status, %lx =\n", Status);
199                 break;
200         }
201     } while (TRUE);
202 }
203