[NTOS:MM] Fix ViewSize parameter passed to MiInsertVadEx() from MiCreatePebOrTeb()
[reactos.git] / ntoskrnl / config / cmdelay.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/cmdelay.c
5 * PURPOSE: Routines for handling delay close and allocate.
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 WORK_QUEUE_ITEM CmpDelayDerefKCBWorkItem;
18
19 ULONG CmpDelayedCloseSize = 2048;
20 ULONG CmpDelayedCloseElements;
21 KGUARDED_MUTEX CmpDelayedCloseTableLock;
22 BOOLEAN CmpDelayCloseWorkItemActive;
23 WORK_QUEUE_ITEM CmpDelayCloseWorkItem;
24 LIST_ENTRY CmpDelayedLRUListHead;
25 ULONG CmpDelayCloseIntervalInSeconds = 5;
26 KDPC CmpDelayCloseDpc;
27 KTIMER CmpDelayCloseTimer;
28
29 KGUARDED_MUTEX CmpDelayDerefKCBLock;
30 BOOLEAN CmpDelayDerefKCBWorkItemActive;
31 LIST_ENTRY CmpDelayDerefKCBListHead;
32 ULONG CmpDelayDerefKCBIntervalInSeconds = 5;
33 KDPC CmpDelayDerefKCBDpc;
34 KTIMER CmpDelayDerefKCBTimer;
35
36 /* FUNCTIONS *****************************************************************/
37
38 _Function_class_(KDEFERRED_ROUTINE)
39 VOID
40 NTAPI
41 CmpDelayCloseDpcRoutine(IN PKDPC Dpc,
42 IN PVOID DeferredContext,
43 IN PVOID SystemArgument1,
44 IN PVOID SystemArgument2)
45 {
46 /* Sanity check */
47 ASSERT(CmpDelayCloseWorkItemActive);
48
49 /* Queue the work item */
50 ExQueueWorkItem(&CmpDelayCloseWorkItem, DelayedWorkQueue);
51 }
52
53 _Function_class_(WORKER_THREAD_ROUTINE)
54 VOID
55 NTAPI
56 CmpDelayCloseWorker(IN PVOID Context)
57 {
58 PCM_DELAYED_CLOSE_ENTRY ListEntry;
59 ULONG i, ConvKey;
60 PAGED_CODE();
61
62 /* Sanity check */
63 ASSERT(CmpDelayCloseWorkItemActive);
64
65 /* Lock the registry */
66 CmpLockRegistry();
67
68 /* Acquire the delayed close table lock */
69 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock);
70
71 /* Iterate */
72 for (i = 0; i < (CmpDelayedCloseSize >> 2); i++)
73 {
74 /* Break out of the loop if there is nothing to process */
75 if (CmpDelayedCloseElements <= CmpDelayedCloseSize) break;
76
77 /* Sanity check */
78 ASSERT(!IsListEmpty(&CmpDelayedLRUListHead));
79
80 /* Get the entry */
81 ListEntry = CONTAINING_RECORD(CmpDelayedLRUListHead.Blink,
82 CM_DELAYED_CLOSE_ENTRY,
83 DelayedLRUList);
84
85 /* Save the ConvKey value of the KCB */
86 ConvKey = ListEntry->KeyControlBlock->ConvKey;
87
88 /* Release the delayed close table lock */
89 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock);
90
91 /* Acquire the KCB lock */
92 CmpAcquireKcbLockExclusiveByKey(ConvKey);
93
94 /* Reacquire the delayed close table lock */
95 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock);
96
97 /* Get the entry */
98 ListEntry = CONTAINING_RECORD(CmpDelayedLRUListHead.Blink,
99 CM_DELAYED_CLOSE_ENTRY,
100 DelayedLRUList);
101
102 /* Is the entry we have still the first one? */
103 if (CmpDelayedCloseElements <= CmpDelayedCloseSize)
104 {
105 /* No, someone already inserted an entry there */
106 CmpReleaseKcbLockByKey(ConvKey);
107 break;
108 }
109
110 /* Is it a different entry? */
111 if (ConvKey != ListEntry->KeyControlBlock->ConvKey)
112 {
113 /* Release the delayed close table lock */
114 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock);
115
116 /* Release the KCB lock */
117 CmpReleaseKcbLockByKey(ConvKey);
118
119 /* Reacquire the delayed close table lock */
120 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock);
121
122 /* Iterate again */
123 continue;
124 }
125
126 /* Remove it from the end of the list */
127 ListEntry =
128 (PCM_DELAYED_CLOSE_ENTRY)RemoveTailList(&CmpDelayedLRUListHead);
129
130 /* Get the containing entry */
131 ListEntry = CONTAINING_RECORD(ListEntry,
132 CM_DELAYED_CLOSE_ENTRY,
133 DelayedLRUList);
134
135 /* Process the entry */
136 if ((ListEntry->KeyControlBlock->RefCount) ||
137 (ListEntry->KeyControlBlock->DelayedCloseIndex))
138 {
139 /* Add it to the beginning of the list */
140 InsertHeadList(&CmpDelayedLRUListHead, &ListEntry->DelayedLRUList);
141
142 /* Release the delayed close table lock */
143 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock);
144 }
145 else
146 {
147 /* Release the delayed close table lock */
148 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock);
149
150 /* Zero out the DelayCloseEntry pointer */
151 ListEntry->KeyControlBlock->DelayCloseEntry = NULL;
152
153 /* Cleanup the KCB cache */
154 CmpCleanUpKcbCacheWithLock(ListEntry->KeyControlBlock, FALSE);
155
156 /* Free the delay item */
157 CmpFreeDelayItem(ListEntry);
158
159 /* Decrement delayed close elements count */
160 InterlockedDecrement((PLONG)&CmpDelayedCloseElements);
161 }
162
163 /* Release the KCB lock */
164 CmpReleaseKcbLockByKey(ConvKey);
165
166 /* Reacquire the delayed close table lock */
167 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock);
168 }
169
170 if (CmpDelayedCloseElements <= CmpDelayedCloseSize)
171 {
172 /* We're not active anymore */
173 CmpDelayCloseWorkItemActive = FALSE;
174 }
175 else
176 {
177 /* We didn't process all things, so reschedule for the next time */
178 CmpArmDelayedCloseTimer();
179 }
180
181 /* Release the delayed close table lock */
182 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock);
183
184 /* Unlock the registry */
185 CmpUnlockRegistry();
186 }
187
188 VOID
189 NTAPI
190 INIT_FUNCTION
191 CmpInitializeDelayedCloseTable(VOID)
192 {
193
194 /* Setup the delayed close lock */
195 KeInitializeGuardedMutex(&CmpDelayedCloseTableLock);
196
197 /* Setup the work item */
198 ExInitializeWorkItem(&CmpDelayCloseWorkItem, CmpDelayCloseWorker, NULL);
199
200 /* Setup the list head */
201 InitializeListHead(&CmpDelayedLRUListHead);
202
203 /* Setup the DPC and its timer */
204 KeInitializeDpc(&CmpDelayCloseDpc, CmpDelayCloseDpcRoutine, NULL);
205 KeInitializeTimer(&CmpDelayCloseTimer);
206 }
207
208 _Function_class_(KDEFERRED_ROUTINE)
209 VOID
210 NTAPI
211 CmpDelayDerefKCBDpcRoutine(IN PKDPC Dpc,
212 IN PVOID DeferredContext,
213 IN PVOID SystemArgument1,
214 IN PVOID SystemArgument2)
215 {
216 /* Sanity check */
217 ASSERT(CmpDelayDerefKCBWorkItemActive);
218
219 /* Queue the work item */
220 ExQueueWorkItem(&CmpDelayDerefKCBWorkItem, DelayedWorkQueue);
221 }
222
223 _Function_class_(WORKER_THREAD_ROUTINE)
224 VOID
225 NTAPI
226 CmpDelayDerefKCBWorker(IN PVOID Context)
227 {
228 PCM_DELAY_DEREF_KCB_ITEM Entry;
229 PAGED_CODE();
230
231 /* Sanity check */
232 ASSERT(CmpDelayDerefKCBWorkItemActive);
233
234 /* Lock the registry and and list lock */
235 CmpLockRegistry();
236 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock);
237
238 /* Check if the list is empty */
239 while (!IsListEmpty(&CmpDelayDerefKCBListHead))
240 {
241 /* Grab an entry */
242 Entry = (PVOID)RemoveHeadList(&CmpDelayDerefKCBListHead);
243
244 /* We can release the lock now */
245 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock);
246
247 /* Now grab the actual entry */
248 Entry = CONTAINING_RECORD(Entry, CM_DELAY_DEREF_KCB_ITEM, ListEntry);
249 Entry->ListEntry.Flink = Entry->ListEntry.Blink = NULL;
250
251 /* Dereference and free */
252 CmpDereferenceKeyControlBlock(Entry->Kcb);
253 CmpFreeDelayItem(Entry);
254
255 /* Lock the list again */
256 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock);
257 }
258
259 /* We're done */
260 CmpDelayDerefKCBWorkItemActive = FALSE;
261 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock);
262 CmpUnlockRegistry();
263 }
264
265 VOID
266 NTAPI
267 INIT_FUNCTION
268 CmpInitDelayDerefKCBEngine(VOID)
269 {
270 /* Initialize lock and list */
271 KeInitializeGuardedMutex(&CmpDelayDerefKCBLock);
272 InitializeListHead(&CmpDelayDerefKCBListHead);
273
274 /* Setup the work item */
275 ExInitializeWorkItem(&CmpDelayDerefKCBWorkItem,
276 CmpDelayDerefKCBWorker,
277 NULL);
278
279 /* Setup the DPC and timer for it */
280 KeInitializeDpc(&CmpDelayDerefKCBDpc, CmpDelayDerefKCBDpcRoutine, NULL);
281 KeInitializeTimer(&CmpDelayDerefKCBTimer);
282 }
283
284 VOID
285 NTAPI
286 CmpDelayDerefKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
287 {
288 LONG OldRefCount, NewRefCount;
289 LARGE_INTEGER Timeout;
290 PCM_DELAY_DEREF_KCB_ITEM Entry;
291 PAGED_CODE();
292 CMTRACE(CM_REFERENCE_DEBUG,
293 "%s - Dereferencing KCB: %p\n", __FUNCTION__, Kcb);
294
295 /* Get the previous reference count */
296 OldRefCount = *(PLONG)&Kcb->RefCount;
297 NewRefCount = OldRefCount - 1;
298 if (((NewRefCount & 0xFFFF) > 0) &&
299 (InterlockedCompareExchange((PLONG)&Kcb->RefCount,
300 NewRefCount,
301 OldRefCount) == OldRefCount))
302 {
303 /* KCB still had references, so we're done */
304 return;
305 }
306
307 /* Allocate a delay item */
308 Entry = CmpAllocateDelayItem();
309 if (!Entry) return;
310
311 /* Set the KCB */
312 Entry->Kcb = Kcb;
313
314 /* Acquire the delayed deref table lock */
315 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock);
316
317 /* Insert the entry into the list */
318 InsertTailList(&CmpDelayDerefKCBListHead, &Entry->ListEntry);
319
320 /* Check if we need to enable anything */
321 if (!CmpDelayDerefKCBWorkItemActive)
322 {
323 /* Yes, we have no work item, setup the interval */
324 CmpDelayDerefKCBWorkItemActive = TRUE;
325 Timeout.QuadPart = CmpDelayDerefKCBIntervalInSeconds * -10000000;
326 KeSetTimer(&CmpDelayDerefKCBTimer, Timeout, &CmpDelayDerefKCBDpc);
327 }
328
329 /* Release the table lock */
330 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock);
331 }
332
333 VOID
334 NTAPI
335 CmpArmDelayedCloseTimer(VOID)
336 {
337 LARGE_INTEGER Timeout;
338 PAGED_CODE();
339
340 /* Set the worker active */
341 CmpDelayCloseWorkItemActive = TRUE;
342
343 /* Setup the interval */
344 Timeout.QuadPart = CmpDelayCloseIntervalInSeconds * -10000000;
345 KeSetTimer(&CmpDelayCloseTimer, Timeout, &CmpDelayCloseDpc);
346 }
347
348 VOID
349 NTAPI
350 CmpAddToDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb,
351 IN BOOLEAN LockHeldExclusively)
352 {
353 ULONG i;
354 ULONG OldRefCount, NewRefCount;
355 PCM_DELAYED_CLOSE_ENTRY Entry;
356 PAGED_CODE();
357
358 /* Sanity check */
359 CMP_ASSERT_KCB_LOCK(Kcb);
360
361 /* Make sure it's valid */
362 if (Kcb->DelayedCloseIndex != CmpDelayedCloseSize) ASSERT(FALSE);
363
364 /* Sanity checks */
365 ASSERT(Kcb->RefCount == 0);
366 ASSERT(IsListEmpty(&Kcb->KeyBodyListHead) == TRUE);
367 for (i = 0; i < 4; i++) ASSERT(Kcb->KeyBodyArray[i] == NULL);
368
369 /* Allocate a delay item */
370 Entry = CmpAllocateDelayItem();
371 if (!Entry)
372 {
373 /* Cleanup immediately */
374 CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively);
375 return;
376 }
377
378 /* Sanity check */
379 if (Kcb->InDelayClose) ASSERT(FALSE);
380
381 /* Get the previous reference count */
382 OldRefCount = *(PLONG)&Kcb->InDelayClose;
383 ASSERT(OldRefCount == 0);
384
385 /* Write the new one */
386 NewRefCount = 1;
387 if (InterlockedCompareExchange((PLONG)&Kcb->InDelayClose,
388 NewRefCount,
389 OldRefCount) != OldRefCount)
390 {
391 /* Sanity check */
392 ASSERT(FALSE);
393 }
394
395 /* Reset the delayed close index */
396 Kcb->DelayedCloseIndex = 0;
397
398 /* Set up the close entry */
399 Kcb->DelayCloseEntry = Entry;
400 Entry->KeyControlBlock = Kcb;
401
402 /* Increase the number of elements */
403 InterlockedIncrement((PLONG)&CmpDelayedCloseElements);
404
405 /* Acquire the delayed close table lock */
406 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock);
407
408 /* Insert the entry into the list */
409 InsertHeadList(&CmpDelayedLRUListHead, &Entry->DelayedLRUList);
410
411 /* Check if we need to enable anything */
412 if ((CmpDelayedCloseElements > CmpDelayedCloseSize) &&
413 !(CmpDelayCloseWorkItemActive))
414 {
415 /* Yes, we have too many elements to close, and no work item */
416 CmpArmDelayedCloseTimer();
417 }
418
419 /* Release the table lock */
420 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock);
421 }
422
423 VOID
424 NTAPI
425 CmpRemoveFromDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb)
426 {
427 PCM_DELAYED_CLOSE_ENTRY Entry;
428 ULONG NewRefCount, OldRefCount;
429 PAGED_CODE();
430
431 /* Sanity checks */
432 CMP_ASSERT_KCB_LOCK(Kcb);
433 if (Kcb->DelayedCloseIndex == CmpDelayedCloseSize) ASSERT(FALSE);
434
435 /* Get the entry and lock the table */
436 Entry = Kcb->DelayCloseEntry;
437 ASSERT(Entry);
438 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock);
439
440 /* Remove the entry */
441 RemoveEntryList(&Entry->DelayedLRUList);
442
443 /* Release the lock */
444 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock);
445
446 /* Free the entry */
447 CmpFreeDelayItem(Entry);
448
449 /* Reduce the number of elements */
450 InterlockedDecrement((PLONG)&CmpDelayedCloseElements);
451
452 /* Sanity check */
453 if (!Kcb->InDelayClose) ASSERT(FALSE);
454
455 /* Get the previous reference count */
456 OldRefCount = *(PLONG)&Kcb->InDelayClose;
457 ASSERT(OldRefCount == 1);
458
459 /* Write the new one */
460 NewRefCount = 0;
461 if (InterlockedCompareExchange((PLONG)&Kcb->InDelayClose,
462 NewRefCount,
463 OldRefCount) != OldRefCount)
464 {
465 /* Sanity check */
466 ASSERT(FALSE);
467 }
468
469 /* Remove the link to the entry */
470 Kcb->DelayCloseEntry = NULL;
471
472 /* Set new delay size and remove the delete flag */
473 Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
474 }