VOID NTAPI CmpLazyFlushWorker(IN PVOID Parameter) { BOOLEAN ForceFlush, Result, MoreWork = FALSE; ULONG DirtyCount = 0; PAGED_CODE(); /* Don't do anything if lazy flushing isn't enabled yet */ if (CmpHoldLazyFlush) { DPRINT1("Lazy flush held. Bye bye.\n"); CmpLazyFlushPending = FALSE; return; } /* Check if we are forcing a flush */ ForceFlush = CmpForceForceFlush; if (ForceFlush) { DPRINT("Forcing flush.\n"); /* Lock the registry exclusively */ CmpLockRegistryExclusive(); } else { DPRINT("Not forcing flush.\n"); /* Starve writers before locking */ InterlockedIncrement(&CmpFlushStarveWriters); CmpLockRegistry(); } /* Flush the next hive */ MoreWork = CmpDoFlushNextHive(ForceFlush, &Result, &DirtyCount); if (!MoreWork) { /* We're done */ InterlockedIncrement((PLONG)&CmpLazyFlushCount); } /* Check if we have starved writers */ if (!ForceFlush) InterlockedDecrement(&CmpFlushStarveWriters); /* Not pending anymore, release the registry lock */ CmpLazyFlushPending = FALSE; CmpUnlockRegistry(); DPRINT("Lazy flush done. More work to be done: %s. Entries still dirty: %u.\n", MoreWork ? "Yes" : "No", DirtyCount); if (MoreWork) { /* Relaunch the flush timer, so the remaining hives get flushed */ CmpLazyFlush(); } }
VOID NTAPI CmpLazyFlushWorker(IN PVOID Parameter) { BOOLEAN ForceFlush, Result, MoreWork = FALSE; ULONG DirtyCount = 0; PAGED_CODE(); /* Don't do anything if lazy flushing isn't enabled yet */ if (CmpHoldLazyFlush) return; /* Check if we are forcing a flush */ ForceFlush = CmpForceForceFlush; if (ForceFlush) { /* Lock the registry exclusively */ CmpLockRegistryExclusive(); } else { /* Do a normal lock */ CmpLockRegistry(); InterlockedIncrement(&CmpFlushStarveWriters); } /* Flush the next hive */ MoreWork = CmpDoFlushNextHive(ForceFlush, &Result, &DirtyCount); if (!MoreWork) { /* We're done */ InterlockedIncrement((PLONG)&CmpLazyFlushCount); } /* Check if we have starved writers */ if (!ForceFlush) InterlockedDecrement(&CmpFlushStarveWriters); /* Not pending anymore, release the registry lock */ CmpLazyFlushPending = FALSE; CmpUnlockRegistry(); /* Check if we need to flush another hive */ if ((MoreWork) || (DirtyCount)) CmpLazyFlush(); }
VOID CmpDelayDerefKCBWorker( IN PVOID Parameter ) /*++ Routine Description: Arguments: Parameter - not used. Return Value: None. --*/ { PCM_DELAY_DEREF_KCB_ITEM DelayItem; PCMHIVE CmHive = NULL; BOOLEAN DoUnloadCheck = FALSE; CM_PAGED_CODE(); UNREFERENCED_PARAMETER (Parameter); ASSERT(CmpDelayDerefKCBWorkItemActive); BEGIN_LOCK_CHECKPOINT; CmpLockRegistry(); LOCK_KCB_DELAY_DEREF_LIST(); while( !CmpIsListEmpty(&CmpDelayDerefKCBListHead) ) { // // pull it out of the list // DelayItem = (PCM_DELAY_DEREF_KCB_ITEM)RemoveHeadList(&CmpDelayDerefKCBListHead); UNLOCK_KCB_DELAY_DEREF_LIST(); DelayItem = CONTAINING_RECORD( DelayItem, CM_DELAY_DEREF_KCB_ITEM, ListEntry); CmpClearListEntry(&(DelayItem->ListEntry)); // // take additional precaution in the case the hive has been unloaded and this is the root // DoUnloadCheck = FALSE; if( !DelayItem->Kcb->Delete ) { CmHive = (PCMHIVE)CONTAINING_RECORD(DelayItem->Kcb->KeyHive, CMHIVE, Hive); if( IsHiveFrozen(CmHive) ) { // // unload is pending for this hive; // DoUnloadCheck = TRUE; } } // // now deref and free // CmpDereferenceKeyControlBlock(DelayItem->Kcb); CmpDelayDerefKCBFreeEntry(DelayItem); // // if we dropped a reference inside a frozen hive, we may need to unload the hive // if( DoUnloadCheck == TRUE ) { CmpDoQueueLateUnloadWorker(CmHive); } LOCK_KCB_DELAY_DEREF_LIST(); } // // signal that we have finished our work. // CmpDelayDerefKCBWorkItemActive = FALSE; UNLOCK_KCB_DELAY_DEREF_LIST(); CmpUnlockRegistry(); END_LOCK_CHECKPOINT; }
VOID CmpDelayCloseWorker( IN PVOID Parameter ) /*++ Routine Description: The fun part. We need to walk the cache and look for kcbs with refcount == 0 Get the oldest one and kick it out of cache. Arguments: Parameter - not used. Return Value: None. --*/ { PCM_DELAYED_CLOSE_ENTRY DelayedEntry; ULONG ConvKey; ULONG MaxIterations = MAX_DELAY_WORKER_ITERATIONS; CM_PAGED_CODE(); UNREFERENCED_PARAMETER (Parameter); ASSERT(CmpDelayCloseWorkItemActive); BEGIN_LOCK_CHECKPOINT; CmpLockRegistry(); // // process kick out every entry with RefCount == 0 && DelayCloseIndex == 0 // ignore the others; we only do this while there is excess of delay - close kcbs // LOCK_DELAY_CLOSE(); while( (CmpDelayedCloseElements > CmpDelayedCloseSize) && (MaxIterations--) ) { ASSERT( !CmpIsListEmpty(&CmpDelayedLRUListHead) ); // // We first need to get the hash entry and attempt to lock it. // DelayedEntry = (PCM_DELAYED_CLOSE_ENTRY)(CmpDelayedLRUListHead.Blink); DelayedEntry = CONTAINING_RECORD( DelayedEntry, CM_DELAYED_CLOSE_ENTRY, DelayedLRUList); ConvKey = DelayedEntry->KeyControlBlock->ConvKey; UNLOCK_DELAY_CLOSE(); // // now lock the hash then recheck if the entry is still first on the list // CmpLockHashEntryExclusive(ConvKey); LOCK_DELAY_CLOSE(); if( CmpDelayedCloseElements <= CmpDelayedCloseSize ) { // // just bail out; no need to kick them out // CmpUnlockHashEntry(ConvKey); break; } DelayedEntry = (PCM_DELAYED_CLOSE_ENTRY)(CmpDelayedLRUListHead.Blink); DelayedEntry = CONTAINING_RECORD( DelayedEntry, CM_DELAYED_CLOSE_ENTRY, DelayedLRUList); if( ConvKey == DelayedEntry->KeyControlBlock->ConvKey ) { // // same hash entry; proceed // pull it out of the list // DelayedEntry = (PCM_DELAYED_CLOSE_ENTRY)RemoveTailList(&CmpDelayedLRUListHead); DelayedEntry = CONTAINING_RECORD( DelayedEntry, CM_DELAYED_CLOSE_ENTRY, DelayedLRUList); CmpClearListEntry(&(DelayedEntry->DelayedLRUList)); if( (DelayedEntry->KeyControlBlock->RefCount == 0) && (DelayedEntry->KeyControlBlock->DelayedCloseIndex == 0) ){ // // free this kcb and the entry // UNLOCK_DELAY_CLOSE(); DelayedEntry->KeyControlBlock->DelayCloseEntry = NULL; CmpCleanUpKcbCacheWithLock(DelayedEntry->KeyControlBlock,FALSE); CmpDelayCloseFreeEntry(DelayedEntry); InterlockedDecrement((PLONG)&CmpDelayedCloseElements); } else { // // put it back at the top // InsertHeadList( &CmpDelayedLRUListHead, &(DelayedEntry->DelayedLRUList) ); UNLOCK_DELAY_CLOSE(); } } else { UNLOCK_DELAY_CLOSE(); } CmpUnlockHashEntry(ConvKey); LOCK_DELAY_CLOSE(); } if( CmpDelayedCloseElements > CmpDelayedCloseSize ) { // // iteration run was too short, there are more elements to process, queue ourselves for later // CmpArmDelayedCloseTimer(); } else { // // signal that we have finished our work. // CmpDelayCloseWorkItemActive = FALSE; } UNLOCK_DELAY_CLOSE(); CmpUnlockRegistry(); END_LOCK_CHECKPOINT; }
VOID CmpDeleteKeyObject( IN PVOID Object ) /*++ Routine Description: This routine interfaces to the NT Object Manager. It is invoked when the last reference to a particular Key object (or Key Root object) is destroyed. If the Key object going away holds the last reference to the extension it is associated with, that extension is destroyed. Arguments: Object - supplies a pointer to a KeyRoot or Key, thus -> KEY_BODY. Return Value: NONE. --*/ { PCM_KEY_CONTROL_BLOCK KeyControlBlock; PCM_KEY_BODY KeyBody; PCMHIVE CmHive = NULL; BOOLEAN DoUnloadCheck = FALSE; CM_PAGED_CODE(); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_FLOW,"CmpDeleteKeyObject: Object = %p\n", Object)); // // HandleClose callback // if ( CmAreCallbacksRegistered() ) { REG_KEY_HANDLE_CLOSE_INFORMATION KeyHandleCloseInfo; KeyHandleCloseInfo.Object = Object; CmpCallCallBacks(RegNtPreKeyHandleClose,&KeyHandleCloseInfo,TRUE,RegNtPostKeyHandleClose,Object); } KeyBody = (PCM_KEY_BODY)Object; BEGIN_LOCK_CHECKPOINT; CmpLockRegistry(); if (KeyBody->Type==KEY_BODY_TYPE) { KeyControlBlock = KeyBody->KeyControlBlock; // // the keybody should be initialized; when kcb is null, something went wrong // between the creation and the dereferenciation of the object // if( KeyControlBlock != NULL ) { // // Clean up any outstanding notifies attached to the KeyBody // CmpFlushNotify(KeyBody,FALSE); // // Remove our reference to the KeyControlBlock, clean it up, perform any // pend-till-final-close operations. // // NOTE: Delete notification is seen at the parent of the deleted key, // not the deleted key itself. If any notify was outstanding on // this key, it was cleared away above us. Only parent/ancestor // keys will see the report. // // // The dereference will free the KeyControlBlock. If the key was deleted, it // has already been removed from the hash table, and relevant notifications // posted then as well. All we are doing is freeing the tombstone. // // If it was not deleted, we're both cutting the kcb out of // the kcb list/tree, AND freeing its storage. // // // Replace this with the definition so we avoid dropping and reacquiring the lock DelistKeyBodyFromKCB(KeyBody,FALSE); // // take additional precaution in the case the hive has been unloaded and this is the root // if( !KeyControlBlock->Delete ) { CmHive = (PCMHIVE)CONTAINING_RECORD(KeyControlBlock->KeyHive, CMHIVE, Hive); if( IsHiveFrozen(CmHive) ) { // // unload is pending for this hive; // DoUnloadCheck = TRUE; } } CmpDelayDerefKeyControlBlock(KeyControlBlock); } } else { // // This must be a predefined handle // some sanity asserts // KeyControlBlock = KeyBody->KeyControlBlock; ASSERT( KeyBody->Type®_PREDEF_HANDLE_MASK); ASSERT( KeyControlBlock->Flags&KEY_PREDEF_HANDLE ); if( KeyControlBlock != NULL ) { CmHive = (PCMHIVE)CONTAINING_RECORD(KeyControlBlock->KeyHive, CMHIVE, Hive); if( IsHiveFrozen(CmHive) ) { // // unload is pending for this hive; we shouldn't put the kcb in the delay // close table // DoUnloadCheck = TRUE; } CmpDereferenceKeyControlBlock(KeyControlBlock); } } // // if a handle inside a frozen hive has been closed, we may need to unload the hive // if( DoUnloadCheck == TRUE ) { CmpDoQueueLateUnloadWorker(CmHive); } CmpUnlockRegistry(); END_LOCK_CHECKPOINT; // // just a notification; disregard the return status // CmPostCallbackNotification(RegNtPostKeyHandleClose,NULL,STATUS_SUCCESS); return; }
VOID CmpLateUnloadHiveWorker( IN PVOID Hive ) /*++ Routine Description: "Late" unloads the hive; If nothing goes badly wrong (i.e. insufficient resources), this function should succeed Arguments: CmHive - the frozen hive to be unloaded Return Value: NONE. --*/ { NTSTATUS Status; HCELL_INDEX Cell; PCM_KEY_CONTROL_BLOCK RootKcb; PCMHIVE CmHive; CM_PAGED_CODE(); // // first, load the registry exclusive // CmpLockRegistryExclusive(); // // hive is the parameter to this worker; make sure we free the work item // allocated by CmpDeleteKeyObject // CmHive = (PCMHIVE)Hive; ASSERT( CmHive->UnloadWorkItem != NULL ); ExFreePool( CmHive->UnloadWorkItem ); // // if this attempt doesn't succeed, mark that we can try another // CmHive->UnloadWorkItem = NULL; ASSERT( !(CmHive->Hive.HiveFlags & HIVE_IS_UNLOADING) ); if( CmHive->Frozen == FALSE ) { // // another thread mounted the exact same hive in the exact same place, hence unfreezing the hive // we've done the cleanup part (free the workitem) nothing more to do. // or hive is already in process of being unloaded // ASSERT( CmHive->RootKcb == NULL ); CmpUnlockRegistry(); return; } // // this is just about the only possible way the hive can get corrupted in between // if( HvShutdownComplete == TRUE ) { // too late to do anything CmpUnlockRegistry(); return; } // // hive should be frozen, otherwise we wouldn't get here // ASSERT( CmHive->Frozen == TRUE ); RootKcb = CmHive->RootKcb; // // root kcb must be valid and has only our "artificial" refcount on it // ASSERT( RootKcb != NULL ); if( RootKcb->RefCount > 1 ) { // // somebody else must've gotten in between dropping/reacquiring the reglock // and opened a handle inside this hive; bad luck, we can't unload // CmpUnlockRegistry(); return; } ASSERT_KCB(RootKcb); Cell = RootKcb->KeyCell; Status = CmUnloadKey(RootKcb,0,CM_UNLOAD_REG_LOCKED_EX); ASSERT( (Status != STATUS_CANNOT_DELETE) && (Status != STATUS_INVALID_PARAMETER) ); if(NT_SUCCESS(Status)) { // CmUnloadKey already released the lock CmpLockRegistry(); CmpDereferenceKeyControlBlock(RootKcb); } CmpUnlockRegistry(); }