Create a branch for network fixes.
[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 LIST_ENTRY CmpFreeDelayItemsListHead;
19
20 ULONG CmpDelayedCloseSize = 2048;
21 ULONG CmpDelayedCloseElements;
22 KGUARDED_MUTEX CmpDelayedCloseTableLock;
23 BOOLEAN CmpDelayCloseWorkItemActive;
24 WORK_QUEUE_ITEM CmpDelayCloseWorkItem;
25 LIST_ENTRY CmpDelayedLRUListHead;
26 ULONG CmpDelayCloseIntervalInSeconds = 5;
27 KDPC CmpDelayCloseDpc;
28 KTIMER CmpDelayCloseTimer;
29
30 KGUARDED_MUTEX CmpDelayDerefKCBLock;
31 BOOLEAN CmpDelayDerefKCBWorkItemActive;
32 LIST_ENTRY CmpDelayDerefKCBListHead;
33 ULONG CmpDelayDerefKCBIntervalInSeconds = 5;
34 KDPC CmpDelayDerefKCBDpc;
35 KTIMER CmpDelayDerefKCBTimer;
36
37 /* FUNCTIONS *****************************************************************/
38
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 VOID
54 NTAPI
55 CmpDelayCloseWorker(IN PVOID Context)
56 {
57 PAGED_CODE();
58
59 /* Sanity check */
60 ASSERT(CmpDelayCloseWorkItemActive);
61
62 /* FIXME: TODO */
63 ASSERT(FALSE);
64 }
65
66 VOID
67 NTAPI
68 CmpInitializeDelayedCloseTable(VOID)
69 {
70
71 /* Setup the delayed close lock */
72 KeInitializeGuardedMutex(&CmpDelayedCloseTableLock);
73
74 /* Setup the work item */
75 ExInitializeWorkItem(&CmpDelayCloseWorkItem, CmpDelayCloseWorker, NULL);
76
77 /* Setup the list head */
78 InitializeListHead(&CmpDelayedLRUListHead);
79
80 /* Setup the DPC and its timer */
81 KeInitializeDpc(&CmpDelayCloseDpc, CmpDelayCloseDpcRoutine, NULL);
82 KeInitializeTimer(&CmpDelayCloseTimer);
83 }
84
85 VOID
86 NTAPI
87 CmpDelayDerefKCBDpcRoutine(IN PKDPC Dpc,
88 IN PVOID DeferredContext,
89 IN PVOID SystemArgument1,
90 IN PVOID SystemArgument2)
91 {
92 /* Sanity check */
93 ASSERT(CmpDelayDerefKCBWorkItemActive);
94
95 /* Queue the work item */
96 ExQueueWorkItem(&CmpDelayDerefKCBWorkItem, DelayedWorkQueue);
97 }
98
99 VOID
100 NTAPI
101 CmpDelayDerefKCBWorker(IN PVOID Context)
102 {
103 PCM_DELAY_DEREF_KCB_ITEM Entry;
104 PAGED_CODE();
105
106 /* Sanity check */
107 ASSERT(CmpDelayDerefKCBWorkItemActive);
108
109 /* Lock the registry and and list lock */
110 CmpLockRegistry();
111 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock);
112
113 /* Check if the list is empty */
114 while (!IsListEmpty(&CmpDelayDerefKCBListHead))
115 {
116 /* Grab an entry */
117 Entry = (PVOID)RemoveHeadList(&CmpDelayDerefKCBListHead);
118
119 /* We can release the lock now */
120 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock);
121
122 /* Now grab the actual entry */
123 Entry = CONTAINING_RECORD(Entry, CM_DELAY_DEREF_KCB_ITEM, ListEntry);
124 Entry->ListEntry.Flink = Entry->ListEntry.Blink = NULL;
125
126 /* Dereference and free */
127 CmpDereferenceKeyControlBlock(Entry->Kcb);
128 CmpFreeDelayItem(Entry);
129
130 /* Lock the list again */
131 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock);
132 }
133
134 /* We're done */
135 CmpDelayDerefKCBWorkItemActive = FALSE;
136 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock);
137 CmpUnlockRegistry();
138 }
139
140 VOID
141 NTAPI
142 CmpInitDelayDerefKCBEngine(VOID)
143 {
144 /* Initialize lock and list */
145 KeInitializeGuardedMutex(&CmpDelayDerefKCBLock);
146 InitializeListHead(&CmpDelayDerefKCBListHead);
147
148 /* Setup the work item */
149 ExInitializeWorkItem(&CmpDelayDerefKCBWorkItem,
150 CmpDelayDerefKCBWorker,
151 NULL);
152
153 /* Setup the DPC and timer for it */
154 KeInitializeDpc(&CmpDelayDerefKCBDpc, CmpDelayDerefKCBDpcRoutine, NULL);
155 KeInitializeTimer(&CmpDelayDerefKCBTimer);
156 }
157
158 VOID
159 NTAPI
160 CmpDelayDerefKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
161 {
162 LONG OldRefCount, NewRefCount;
163 LARGE_INTEGER Timeout;
164 PCM_DELAY_DEREF_KCB_ITEM Entry;
165 PAGED_CODE();
166
167 /* Get the previous reference count */
168 OldRefCount = *(PLONG)&Kcb->RefCount;
169 NewRefCount = OldRefCount - 1;
170 if (((NewRefCount & 0xFFFF) > 0) &&
171 (InterlockedCompareExchange((PLONG)&Kcb->RefCount,
172 NewRefCount,
173 OldRefCount) == OldRefCount))
174 {
175 /* KCB still had references, so we're done */
176 return;
177 }
178
179 /* Allocate a delay item */
180 Entry = CmpAllocateDelayItem();
181 if (!Entry) return;
182
183 /* Set the KCB */
184 Entry->Kcb = Kcb;
185
186 /* Acquire the delayed deref table lock */
187 KeAcquireGuardedMutex(&CmpDelayDerefKCBLock);
188
189 /* Insert the entry into the list */
190 InsertTailList(&CmpDelayDerefKCBListHead, &Entry->ListEntry);
191
192 /* Check if we need to enable anything */
193 if (!CmpDelayDerefKCBWorkItemActive)
194 {
195 /* Yes, we have no work item, setup the interval */
196 CmpDelayDerefKCBWorkItemActive = TRUE;
197 Timeout.QuadPart = CmpDelayDerefKCBIntervalInSeconds * -10000000;
198 KeSetTimer(&CmpDelayDerefKCBTimer, Timeout, &CmpDelayDerefKCBDpc);
199 }
200
201 /* Release the table lock */
202 KeReleaseGuardedMutex(&CmpDelayDerefKCBLock);
203 }
204
205 VOID
206 NTAPI
207 CmpArmDelayedCloseTimer(VOID)
208 {
209 LARGE_INTEGER Timeout;
210 PAGED_CODE();
211
212 /* Set the worker active */
213 CmpDelayCloseWorkItemActive = TRUE;
214
215 /* Setup the interval */
216 Timeout.QuadPart = CmpDelayCloseIntervalInSeconds * -10000000;
217 KeSetTimer(&CmpDelayCloseTimer, Timeout, &CmpDelayCloseDpc);
218 }
219
220 VOID
221 NTAPI
222 CmpAddToDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb,
223 IN BOOLEAN LockHeldExclusively)
224 {
225 ULONG i;
226 ULONG OldRefCount, NewRefCount;
227 PCM_DELAYED_CLOSE_ENTRY Entry;
228 PAGED_CODE();
229
230 /* Sanity check */
231 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
232 (CmpTestRegistryLockExclusive() == TRUE));
233
234 /* Make sure it's valid */
235 if (Kcb->DelayedCloseIndex != CmpDelayedCloseSize) ASSERT(FALSE);
236
237 /* Sanity checks */
238 ASSERT(Kcb->RefCount == 0);
239 ASSERT(IsListEmpty(&Kcb->KeyBodyListHead) == TRUE);
240 for (i = 0; i < 4; i++) ASSERT(Kcb->KeyBodyArray[i] == NULL);
241
242 /* Allocate a delay item */
243 Entry = CmpAllocateDelayItem();
244 if (!Entry)
245 {
246 /* Cleanup immediately */
247 CmpCleanUpKcbCacheWithLock(Kcb, LockHeldExclusively);
248 return;
249 }
250
251 /* Sanity check */
252 if (Kcb->InDelayClose) ASSERT(FALSE);
253
254 /* Get the previous reference count */
255 OldRefCount = *(PLONG)&Kcb->InDelayClose;
256 ASSERT(OldRefCount == 0);
257
258 /* Write the new one */
259 NewRefCount = 1;
260 if (InterlockedCompareExchange((PLONG)&Kcb->InDelayClose,
261 NewRefCount,
262 OldRefCount) != OldRefCount)
263 {
264 /* Sanity check */
265 ASSERT(FALSE);
266 }
267
268 /* Reset the delayed close index */
269 Kcb->DelayedCloseIndex = 0;
270
271 /* Set up the close entry */
272 Kcb->DelayCloseEntry = Entry;
273 Entry->KeyControlBlock = Kcb;
274
275 /* Increase the number of elements */
276 InterlockedIncrement((PLONG)&CmpDelayedCloseElements);
277
278 /* Acquire the delayed close table lock */
279 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock);
280
281 /* Insert the entry into the list */
282 InsertHeadList(&CmpDelayedLRUListHead, &Entry->DelayedLRUList);
283
284 /* Check if we need to enable anything */
285 if ((CmpDelayedCloseElements > CmpDelayedCloseSize) &&
286 !(CmpDelayCloseWorkItemActive))
287 {
288 /* Yes, we have too many elements to close, and no work item */
289 CmpArmDelayedCloseTimer();
290 }
291
292 /* Release the table lock */
293 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock);
294 }
295
296 VOID
297 NTAPI
298 CmpRemoveFromDelayedClose(IN PCM_KEY_CONTROL_BLOCK Kcb)
299 {
300 PCM_DELAYED_CLOSE_ENTRY Entry;
301 ULONG NewRefCount, OldRefCount;
302 PAGED_CODE();
303
304 /* Sanity checks */
305 ASSERT((CmpIsKcbLockedExclusive(Kcb) == TRUE) ||
306 (CmpTestRegistryLockExclusive() == TRUE));
307 if (Kcb->DelayedCloseIndex == CmpDelayedCloseSize) ASSERT(FALSE);
308
309 /* Get the entry and lock the table */
310 Entry = Kcb->DelayCloseEntry;
311 ASSERT(Entry);
312 KeAcquireGuardedMutex(&CmpDelayedCloseTableLock);
313
314 /* Remove the entry */
315 RemoveEntryList(&Entry->DelayedLRUList);
316
317 /* Release the lock */
318 KeReleaseGuardedMutex(&CmpDelayedCloseTableLock);
319
320 /* Free the entry */
321 CmpFreeDelayItem(Entry);
322
323 /* Reduce the number of elements */
324 InterlockedDecrement((PLONG)&CmpDelayedCloseElements);
325
326 /* Sanity check */
327 if (!Kcb->InDelayClose) ASSERT(FALSE);
328
329 /* Get the previous reference count */
330 OldRefCount = *(PLONG)&Kcb->InDelayClose;
331 ASSERT(OldRefCount == 1);
332
333 /* Write the new one */
334 NewRefCount = 0;
335 if (InterlockedCompareExchange((PLONG)&Kcb->InDelayClose,
336 NewRefCount,
337 OldRefCount) != OldRefCount)
338 {
339 /* Sanity check */
340 ASSERT(FALSE);
341 }
342
343 /* Remove the link to the entry */
344 Kcb->DelayCloseEntry = NULL;
345
346 /* Set new delay size and remove the delete flag */
347 Kcb->DelayedCloseIndex = CmpDelayedCloseSize;
348 }