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 CODE_SEG("INIT") 189 VOID 190 NTAPI 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 CODE_SEG("INIT") 266 VOID 267 NTAPI 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 } 475