From 3ff9824c8b72f76cd0eb39136d79e4a9e782b58f Mon Sep 17 00:00:00 2001 From: Dibyamartanda Samanta Date: Tue, 21 May 2024 11:38:04 +0200 Subject: [PATCH] [NTOSKRNL:CC] Implementation of Lazy Writer Implemented following function *CcPostWorkQueue *CcScheduleLazyWriteScanEx *CcScheduleLazyWriteScan *CcExceptionFilter *CcPerformReadAhead *CcPostDeferredWrites *CcSetValidData *CcWriteBehind *CcLazyWriteScan --- NTOSKRNL/CC/cclazywriter.cpp | 862 +++++++++++++++++++++++++++++++++++ 1 file changed, 862 insertions(+) create mode 100644 NTOSKRNL/CC/cclazywriter.cpp diff --git a/NTOSKRNL/CC/cclazywriter.cpp b/NTOSKRNL/CC/cclazywriter.cpp new file mode 100644 index 0000000..70086df --- /dev/null +++ b/NTOSKRNL/CC/cclazywriter.cpp @@ -0,0 +1,862 @@ +/* + * PROJECT: Alcyone System Kernel + * LICENSE: BSD Clause 3 + * PURPOSE: Cache Controller:: Lazy Writer + * NT KERNEL: 5.11.9360 + * COPYRIGHT: 2023-2029 Dibymartanda Samanta <> + */ +#include +#define NTDEBUG +#include +#include "ccinternal.hpp" +#include "cclazywriter.hpp" + +extern "C" + +/*Internal Function*/ + +VOID +VECTORCALL +CcPostWorkQueue(IN PWORK_QUEUE_ENTRY WorkItem, + IN PLIST_ENTRY WorkQueue) +{ + PWORK_QUEUE_ITEM ThreadToSpawn = nullptr; + PLIST_ENTRY ListEntry = nullptr; // be aware. + KIRQL CurrentIrql = NULL; + + /* Aquire SpinLock & Insert the Worker in Queue*/ + CurrentIrql = KeAcquireQueuedSpinLock(LockQueueWorkQueueLock); + InsertTailList(WorkQueue, &WorkItem->WorkQueueLinks); + + /* CHeck if new thread to be spawnwed */ + if (!CcQueueThrottle && !IsListEmpty(&CcIdleWorkerThreadList)) + { + + ListEntry = RemoveHeadList(&CcIdleWorkerThreadList); + ThreadToSpawn = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ITEM, List); + + /* Update the number of Active Worker Thread */ + _InterlockedIncrement(&CcNumberActiveWorkerThreads); + } + + KeReleaseQueuedSpinLock(LockQueueWorkQueueLock, CurrentIrql); + + /* Check if thread left to spawn, disable it now since Spinlock is released */ + if (ThreadToSpawn) + { + DBGPRINT(" CcPostWorkQueue: Thread Left to be spwaned even after release of Spinlock!\n"); + ThreadToSpawn->List.Flink = nullptr; + ExQueueWorkItem(ThreadToSpawn, CriticalWorkQueue); + } +} +VOID +NTAPI +CcScheduleLazyWriteScanEx(IN BOOLEAN Delay_Status, IN BOOLEAN Teardowmn_Status) +{ + BOOLEAN Result; + + /* If their no delay , Start Lazy Writer Scan Immidietely */ + if (Delay_Status) + { + LazyWriter.Active = True; + /* Check if Teardown is Active */ + if (Teardowmn_Status) + { + LazyWriter.PendingTeardown = True; + } + result = KeSetTimer(&LazyWriter.ScanTimer, CcNoDelay, &LazyWriter.ScanDpc); + } + /* If It is not running , Start it */ + else if (LazyWriter.ScanActive) + { + result = KeSetTimer(&LazyWriter.ScanTimer, CcIdleDelay, &LazyWriter.ScanDpc); + } + /* If it is already running , Queue for it */ + else + { + result = KeSetTimer(&LazyWriter.ScanTimer, CcFirstDelay, &LazyWriter.ScanDpc); + } + return result; +} + +VOID +NTAPI +CcScheduleLazyWriteScan(IN BOOLEAN NoDelay) +{ + return CcScheduleLazyWriteScanEx(NoDelay, False); +} + +LONG +VECTORCALL +CcExceptionFilter(IN NTSTATUS Status) +{ + LONG Result; + /* Get the Flag Status */ + BOOL NTSTATUS_FLAG = static_cast(FsRtlIsNtstatusExpected(Status)); + if (NTSTATUS_FLAG) + Result = EXCEPTION_EXECUTE_HANDLER; + else + Result = EXCEPTION_CONTINUE_SEARCH; + + DBGPRINT("CcExceptionFilter: Status %X\n", Status); + return Result; +} +VOID +VECTORCALL +CcPerformReadAhead(IN PFILE_OBJECT FileObject) +{ + PETHREAD CurrentThread = PsGetCurrentThread(); + PSHARED_CACHE_MAP SharedCachMap; + PPRIVATE_CACHE_MAP PrivateCachMap; + PVACB Vacb = nullptr; + BOOLEAN LengthIsZero = false; + BOOLEAN ReadAheasStatus = false; + BOOLEAN ReadAheadStatus = false; + BOOLEAN ReadAheadFinishStatus = false; + ULONG_PTR VirtualAdress = nullptr; + LARGE_INTEGER FileOffset = {0}; + LARGE_INTEGER readAheadOffset[2]; + ULONG readAheadLength[2]; + ULONG IsPageNotResident = {0}; + ULONG ReceivedLength = {0}; + ULONG NumberOfPages = {0}; + ULONG Length = {0}; + ULONG OldReadClusterSize = {0}; + UCHAR OldForwardClusterOnly = {0}; + KIRQL LastIRQL = {0}; + + /* GET Cluster History */ + OldForwardClusterOnly = CurrentThread->ForwardClusterOnly; + OldReadClusterSize = CurrentThread->ReadClusterSize; + + SharedCachMap = FileObject->SectionObjectPointer->SharedCacheMap; + + _TRY + { + while (TRUE) + { + LastIRQL = KeAcquireQueuedSpinLock(LockQueueMasterLock); + + PrivateCachMap = FileObject->VirtualAdress; + + /* Check if it is a valid VirtualAdress Adress , if Not Reissue ReadAhead Offesets */ + if (PrivateCachMap != nullptr) + { + KeAcquireSpinLockAtDpcLevel(&PrivateCachMap->ReadAheadSpinLock); + + LengthIsZero = (!(PrivateCachMap->ReadAheadLength[0] | PrivateCachMap->ReadAheadLength[1])); + + readAheadOffset[0].QuadPart = PrivateCachMap->ReadAheadOffset[0].QuadPart; + readAheadOffset[1].QuadPart = PrivateCachMap->ReadAheadOffset[1].QuadPart; + + readAheadLength[0] = PrivateCachMap->ReadAheadLength[0]; + readAheadLength[1] = PrivateCachMap->ReadAheadLength[1]; + + PrivateCachMap->ReadAheadLength[0] = 0; + PrivateCachMap->ReadAheadLength[1] = 0; + + KeReleaseSpinLockFromDpcLevel(&PrivateCachMap->ReadAheadSpinLock); + } + + KeReleaseQueuedSpinLock(LockQueueMasterLock, LastIRQL); + + ReadAheadStatus = (*SharedCachMap->Callbacks->AcquireForReadAhead)(SharedCachMap->LazyWriteContext, TRUE); + + if (!PrivateCachMap || LengthIsZero || !ReadAheadStatus) + break; + + for (auto i = 0; i <= 1; i++) + { + FileOffset = readAheadOffset[i]; + Length = readAheadLength[i]; + /* Check if Cache Can be read Ahead */ + if (Length && FileOffset.QuadPart <= SharedCachMap->FileSize.QuadPart) + { + ReadAheasStatus = TRUE; + + if (SharedCachMap->FileSize.QuadPart <= (FileOffset.QuadPart + (LONGLONG)Length)) + { + Length = (SharedCachMap->FileSize.QuadPart - FileOffset.QuadPart); + ReadAheadFinishStatus = TRUE; + } + + if (Length > 0x800000) + Length = 0x800000; // Make a Define and move to header + + while (Length != NULL) + { + VirtualAdress = CcGetVirtualAddress(SharedCachMap, FileOffset, &Vacb, &ReceivedLength); + + if (ReceivedLength > Length) + ReceivedLength = Length; + + for (NumberOfPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(VirtualAdress, ReceivedLength); + NumberOfPages; NumberOfPages--) + { + CurrentThread->ForwardClusterOnly = 1; + + if (NumberOfPages > 0x10) + CurrentThread->ReadClusterSize = 0xF; + else + CurrentThread->ReadClusterSize = (NumberOfPages - 1); + + IsPageNotResident |= !MmCheckCachedPageState(VirtualAdress, FALSE); + + VirtualAdress = static_cast(VirtualAdress + PAGE_SIZE); + } + /*Update History */ + FileOffset.QuadPart += ReceivedLength; + Length -= ReceivedLength; + + CcFreeVirtualAddress(Vacb); + Vacb = nullptr; + } + } + } + + (*SharedCachMap->Callbacks->ReleaseFromReadAhead)(SharedCachMap->LazyWriteContext); + + ReadAheadStatus = FALSE; + } + } + _FINALLY + { + + /* Restore cluster variables */ + CurrentThread->ForwardClusterOnly = OldForwardClusterOnly; + CurrentThread->ReadClusterSize = OldReadClusterSize; + + if (Vacb) + CcFreeVirtualAddress(Vacb); + /* If ReadAheadStatus is False , Release it from Write Context */ + if (ReadAheadStatus) + (*SharedCachMap->Callbacks->ReleaseFromReadAhead)(SharedCachMap->LazyWriteContext); + + LastIRQL = KeAcquireQueuedSpinLock(LockQueueMasterLock); + + PrivateCachMap = FileObject->VirtualAdress; + if (PrivateCachMap) + { + KeAcquireSpinLockAtDpcLevel(&PrivateCachMap->ReadAheadSpinLock); + + RtlInterlockedAndBits(&PrivateCachMap->UlongFlags, ~SHARE_FL_WAITING_TEARDOWN); + + if (ReadAheadFinishStatus && (FileObject->Flags & FO_SEQUENTIAL_ONLY)) + PrivateCachMap->ReadAheadOffset[1].QuadPart = 0; + + if (ReadAheasStatus && !IsPageNotResident) + RtlInterlockedAndBits(&PrivateCachMap->UlongFlags, ~0x20000); + + KeReleaseSpinLockFromDpcLevel(&PrivateCachMap->ReadAheadSpinLock); + } + + KeReleaseQueuedSpinLock(LockQueueMasterLock, LastIRQL); + ObDereferenceObject(FileObject); + LastIRQL = KeAcquireQueuedSpinLock(LockQueueMasterLock); + + _InterlockedDecrement(SharedCachMap->OpenCount); + SharedCachMap->Flags &= ~SHARE_FL_READ_AHEAD; + + /* If a Invalid Cache Map */ + if (!SharedCachMap->OpenCount && !(SharedCachMap->Flags & SHARE_FL_WRITE_QUEUED) && !SharedCachMap->DirtyPages) + { + RemoveEntryList(&SharedCachMap->SharedCacheMapLinks); + InsertTailList(&CcDirtySharedCacheMapList.SharedCacheMapLinks, &SharedCachMap->SharedCacheMapLinks); + + LazyWriter.OtherWork = TRUE; + + if (!LazyWriter.ScanActive) + CcScheduleLazyWriteScan(FALSE); + } + + KeReleaseQueuedSpinLock(LockQueueMasterLock, LastIRQL); + } +} +VOID +NTAPI +CcPostDeferredWrites(VOID) +{ + PDEFERRED_WRITE DeferredWrites = nullptr; + PLIST_ENTRY Entry = nullptr; + ULONG BytesToWrite = {0}; + KIRQL OldIrql = {0}; + + while (TRUE) + { + + /* Acquire the spin lock to protect concurrent access to shared data structures */ + KeAcquireSpinLock(&CcDeferredWriteSpinLock, &OldIrql); + + if (!IsListEmpty(&CcDeferredWrites)) + { + Entry = CcDeferredWrites.Flink; + while (true) + { + DeferredWrites = CONTAINING_RECORD(Entry, DEFERRED_WRITE, DeferredWriteLinks); + + /* Calculate the total bytes to write */ + BytesToWrite += DeferredWrites->BytesToWrite; + + /* BytesToWrite Must not overflow Defferwrite Limits */ + if (BytesToWrite < DeferredWrites->BytesToWrite) + { + DeferredWrites = nullptr; + break; + } + + /* if writing to the file object is possible */ + if (CcCanIWrite(DeferredWrites->FileObject, BytesToWrite, FALSE, 0xFE)) + { + /*Remove the entry from the deferred writes list if writing is possible */ + RemoveEntryList(&DeferredWrites->DeferredWriteLinks); + break; + } + + /* Check if the limit of modified pages has been reached */ + if (!DeferredWrites->LimitModifiedPages) + { + DeferredWrites = nullptr; + break; + } + + /* Decrement the bytes to write and reset DeferredWrites*/ + BytesToWrite -= DeferredWrites->BytesToWrite; + DeferredWrites = nullptr; + + /* Move to the next entry in the list */ + Entry = Entry->Flink; + } + + /* Release the spin lock */ + KeReleaseSpinLock(&CcDeferredWriteSpinLock, OldIrql); + + /* If Defferwrite List is null get out of loop */ + if (!DeferredWrites) + break; + + if (DeferredWrites->Event) + { + KeSetEvent(DeferredWrites->Event, IO_NO_INCREMENT, FALSE); + continue; + } + + /*execute the post routine, and free memory */ + + DeferredWrites->PostRoutine(DeferredWrites->Context1, DeferredWrites->Context2); + ExFreePoolWithTag(DeferredWrites, 0); + } + } +} + +NTSTATUS +NTAPI +CcSetValidData(IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileSize) +{ + FILE_END_OF_FILE_INFORMATION FileInfo; // Structure to hold end-of-file information + PDEVICE_OBJECT DeviceObject; // Pointer to the device object associated with the file + PIO_STACK_LOCATION IoStack; // Pointer to an I/O stack location structure + IO_STATUS_BLOCK IoStatus; // I/O status block structure + KEVENT Event; // Kernel event object for synchronization + PIRP Irp; // Pointer to an I/O request packet structure + NTSTATUS Status; // Status of the operation + + /*Initialize a kernel event object for synchronization */ + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + /* Retrieve the device object associated with the file*/ + DeviceObject = IoGetRelatedDeviceObject(FileObject); + + /* Initialize FileInfo structure with the new file size */ + FileInfo.EndOfFile.QuadPart = FileSize->QuadPart; + + /* Allocate an I/O request packet (IRP) */ + Irp = IoAllocateIrp(DeviceObject->StackSize, 0); + if (!Irp) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + /* Set IRP parameters */ + Irp->AssociatedIrp.SystemBuffer = &FileInfo; + Irp->UserIosb = &IoStatus; + Irp->UserEvent = &Event; + Irp->Flags = (IRP_SYNCHRONOUS_PAGING_IO | IRP_PAGING_IO); + Irp->RequestorMode = KernelMode; + Irp->Tail.Overlay.Thread = PsGetCurrentThread(); + Irp->Tail.Overlay.OriginalFileObject = FileObject; + + /* Get the next IRP stack location and set up IRP parameters */ + IoStack = IoGetNextIrpStackLocation(Irp); + IoStack->MajorFunction = IRP_MJ_SET_INFORMATION; + IoStack->FileObject = FileObject; + IoStack->DeviceObject = DeviceObject; + IoStack->Parameters.SetFile.Length = sizeof(FILE_END_OF_FILE_INFORMATION); + IoStack->Parameters.SetFile.FileInformationClass = FileEndOfFileInformation; + IoStack->Parameters.SetFile.FileObject = nullptr; + IoStack->Parameters.SetFile.AdvanceOnly = 1; + + /* Call the device driver to handle the IRP */ + Status = IoCallDriver(DeviceObject, Irp); + + /*If the operation is pending, wait for it to complete*/ + if (Status == STATUS_PENDING) + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, nullptr); + + /* Handle completion and error handling */ + if (!NT_SUCCESS(Status)) + { + IoStatus.Status = Status; + } + + /* Return the IOstatus of the operation */ + return IoStatus.Status; +} +VOID +VECTORCALL +CcWriteBehind(IN PSHARED_CACHE_MAP SharedMap, + IN IO_STATUS_BLOCK * OutIoStatus) +{ + KLOCK_QUEUE_HANDLE LockHandle; + LARGE_INTEGER validDataLength = {0}; + ULONG ActivePage = {0}; + ULONG TargetPages = {0} ; + KIRQL OldIrql = {0}; + BOOLEAN IsVacbLocked = false; + BOOLEAN IsCancelWait = false; + NTSTATUS Status; + + PVACB ActiveVacb = nullptr; + PUNICODE_STRING FileName = nullptr; + + /*Acquire lazy writer's lock for lazy write*/ + if (!(*SharedMap->Callbacks->AcquireForLazyWrite)(SharedMap->LazyWriteContext, TRUE)) + { + /* If acquisition fails, release LockQueueMasterLock and set STATUS_FILE_LOCK_CONFLICT */ + OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); + SharedMap->Flags &= ~0x20; // Clear the flag indicating wait for Lazy Writer + KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); + + OutIoStatus->Status = STATUS_FILE_LOCK_CONFLICT; + return; + } + + /* Acquire BcbSpinLock and LockQueueMasterLock */ + KeAcquireInStackQueuedSpinLock(&SharedMap->BcbSpinLock, &LockHandle); + KeAcquireQueuedSpinLockAtDpcLevel(&KeGetCurrentPrcb()->LockQueue[LockQueueMasterLock]); + + /*Determine if there are few dirty pages or no open references */ + if (SharedMap->DirtyPages <= 1 || !SharedMap->OpenCount) + { + /*Acquire ActiveVacbSpinLock to handle ActiveVacb*/ + KeAcquireSpinLockAtDpcLevel(&SharedMap->ActiveVacbSpinLock); + + /*Retrieve ActiveVacb and related information if it exists*/ + ActiveVacb = SharedMap->ActiveVacb; + if (ActiveVacb) + { + ActivePage = SharedMap->ActivePage; + SharedMap->ActiveVacb = 0; + IsVacbLocked = ((SharedMap->Flags & SHARE_FL_VACB_LOCKED) != 0); + } + + /*Release ActiveVacbSpinLock*/ + KeReleaseSpinLockFromDpcLevel(&SharedMap->ActiveVacbSpinLock); + } + + /*Increment OpenCount*/ + _InterlockedIncrement(SharedMap->OpenCount); + + /* Update Mbcb information if it exists*/ + if (SharedMap->Mbcb) + { + TargetPages = SharedMap->Mbcb->DirtyPages; + + if (ActiveVacb) + _InterlockedIncrement(TargetPages); + + /*Determine PagesToWrite based on CcPagesYetToWrite and TargetPages*/ + if (TargetPages > CcPagesYetToWrite) + SharedMap->Mbcb->PagesToWrite = CcPagesYetToWrite; + else + SharedMap->Mbcb->PagesToWrite = TargetPages; + } + + /*Release LockQueueMasterLock and BcbSpinLock*/ + KeReleaseQueuedSpinLockFromDpcLevel(&KeGetCurrentPrcb()->LockQueue[LockQueueMasterLock]); + KeReleaseInStackQueuedSpinLock(&LockHandle); + + /* Free ActiveVacb if it exists*/ + if (ActiveVacb) + CcFreeActiveVacb(SharedMap, ActiveVacb, ActivePage, IsVacbLocked); + + /* Flush cache to disk*/ + CcFlushCache(SharedMap->FileObject->SectionObjectPointer, &CcNoDelay, 1, OutIoStatus); + + /*Release lazy writer's lock*/ + (*SharedMap->Callbacks->ReleaseFromLazyWrite)(SharedMap->LazyWriteContext); + + /* Check if Status Verification Failed */ + if (!NT_SUCCESS(OutIoStatus->Status) && OutIoStatus->Status != STATUS_VERIFY_REQUIRED) + IsCancelWait = TRUE; + + if (!NT_SUCCESS(OutIoStatus->Status) && OutIoStatus->Status != STATUS_VERIFY_REQUIRED && + OutIoStatus->Status != STATUS_FILE_LOCK_CONFLICT && + OutIoStatus->Status != STATUS_ENCOUNTERED_WRITE_IN_PROGRESS) + { + /* Prepare for error notification and log*/ + POBJECT_NAME_INFORMATION FileNameInfo = nullptr; + NTSTATUS status; + + /*Retrieve IOSTATUS */ + status = IoQueryFileDosDeviceName(SharedMap->FileObject, &FileNameInfo); + + if (status = STATUS_SUCCESS) + { + IoRaiseInformationalHardError(STATUS_LOST_WRITEBEHIND_DATA, &FileNameInfo->Name, nullptr); + } + else + { + FileName = &SharedMap->FileObject->FileName; + + if (FileName->Length && FileName->MaximumLength && FileName->Buffer) + IoRaiseInformationalHardError(STATUS_LOST_WRITEBEHIND_DATA, FileName, nullptr); + } + + if (FileNameInfo) + ExFreePoolWithTag(FileNameInfo, 0); + } + else if (!IsListEmpty(&CcDeferredWrites)) + { + /*If deferred writes exist, process them */ + CcPostDeferredWrites(); + } + + /*Acquire BcbSpinLock */ + KeAcquireInStackQueuedSpinLock(&SharedMap->BcbSpinLock, &LockHandle); + + /* Update ValidDataLength if necessary*/ + Status = STATUS_SUCCESS; + + if (SharedMap->Flags & (0x400 | 0x8000)) + { + if (SharedMap->ValidDataLength.QuadPart <= SharedMap->ValidDataGoal.QuadPart && + SharedMap->ValidDataLength.QuadPart != 0x7FFFFFFFFFFFFFFF && SharedMap->FileSize.QuadPart) + { + /* Get flushed valid data and set ValidDataLength if necessary */ + validDataLength = CcGetFlushedValidData(SharedMap->FileObject->SectionObjectPointer, TRUE); + + if (validDataLength.QuadPart >= SharedMap->ValidDataLength.QuadPart) + { + KeReleaseInStackQueuedSpinLock(&LockHandle); + Status = CcSetValidData(SharedMap->FileObject, &validDataLength); + KeAcquireInStackQueuedSpinLock(&SharedMap->BcbSpinLock, &LockHandle); + + if (NT_SUCCESS(Status)) + SharedMap->ValidDataLength = validDataLength; + } + } + } + + /* Release BcbSpinLock*/ + KeReleaseInStackQueuedSpinLock(&LockHandle); + + /* Perform cleanup tasks*/ + OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); + InterlockedDecrement(SharedMap->OpenCount); + LockHandle.OldIrql = OldIrql; + + /* Check if there are any remaining open references*/ + if (SharedMap->OpenCount) + goto Exit; + + /*Cleanup if no remaining open references*/ + if (NT_SUCCESS(Status) || + (Status != STATUS_INSUFFICIENT_RESOURCES && Status != STATUS_VERIFY_REQUIRED && + Status != STATUS_FILE_LOCK_CONFLICT && Status != STATUS_ENCOUNTERED_WRITE_IN_PROGRESS)) + { + /* Release LockQueueMasterLock, acquire file object lock, and recheck open count*/ + KeReleaseQueuedSpinLock(LockQueueMasterLock, LockHandle.OldIrql); + FsRtlAcquireFileExclusive(SharedMap->FileObject); + + LockHandle.OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); + + /*Delete SharedCacheMap if no open references remain*/ + if (!SharedMap->OpenCount) + { + if (!SharedMap->DirtyPages || + (!SharedMap->FileSize.QuadPart && !(SharedMap->Flags & SHARE_FL_PIN_ACCESS))) + { + CcDeleteSharedCacheMap(SharedMap, LockHandle.OldIrql, TRUE); + OutIoStatus->Information = 0; + return; + } + } + + /*Release LockQueueMasterLock and file object lock */ + KeReleaseQueuedSpinLock(LockQueueMasterLock, LockHandle.OldIrql); + FsRtlReleaseFile(SharedMap->FileObject); + LockHandle.OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); + } + + Exit: + + /*Clear SHARE_FL_WAITING_FLUSH flag if necessary*/ + if (OutIoStatus->Information != 0x8A5E) + SharedMap->Flags &= ~0x20; + + /* Release LockQueueMasterLock*/ + KeReleaseQueuedSpinLock(LockQueueMasterLock, LockHandle.OldIrql); +} +VOID +NTAPI CcLazyWriteScan() +{ + PSHARED_CACHE_MAP FirstMap = nullptr; + PSHARED_CACHE_MAP SharedMap = nullptr; + PGENERAL_LOOKASIDE LookasideList = nullptr; + PWORK_QUEUE_ENTRY WorkItem = nullptr; + PLIST_ENTRY ListEntry = nullptr; + PLIST_ENTRY MapLinks = nullptr; + PKPRCB Prcb = nullptr; + LIST_ENTRY PostWorkList ={0}; + ULONG TargetPages ={0}; + ULONG NextTargetPages; + ULONG DirtyPages; + ULONG counter = 0; + BOOLEAN IsNoPagesToWrite = FALSE; + BOOLEAN IsDoubleScan = FALSE; + KIRQL OldIrql; + + OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); + + // Check if there is no dirty page and no other work pending + if (!CcTotalDirtyPages && !LazyWriter.OtherWork) + { + // Check if there are deferred writes pending + if (!IsListEmpty(&CcDeferredWrites)) + { + KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); + CcPostDeferredWrites(); + CcScheduleLazyWriteScan(FALSE); + } + else + { + // No deferred writes pending, mark lazy write scan as inactive + LazyWriter.ScanActive = 0; + KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); + } + + return; + } + + InitializeListHead(&PostWorkList); + + // Move items from tick work queue to post work list + while (!IsListEmpty(&CcPostTickWorkQueue)) + { + ListEntry = RemoveHeadList(&CcPostTickWorkQueue); + InsertTailList(&PostWorkList, ListEntry); + } + + LazyWriter.OtherWork = FALSE; + + // Calculate target pages based on dirty pages count + if (CcTotalDirtyPages > 8) + TargetPages = (CcTotalDirtyPages / 8); + else + TargetPages = CcTotalDirtyPages; + + DirtyPages = (CcTotalDirtyPages + CcPagesWrittenLastTime); + + // Calculate next target pages for scanning + if (CcDirtyPagesLastScan < DirtyPages) + NextTargetPages = (DirtyPages - CcDirtyPagesLastScan); + else + NextTargetPages = 0; + + NextTargetPages += (CcTotalDirtyPages - TargetPages); + + if (NextTargetPages > CcDirtyPageTarget) + TargetPages += (NextTargetPages - CcDirtyPageTarget); + + CcDirtyPagesLastScan = CcTotalDirtyPages; + CcPagesWrittenLastTime = TargetPages; + CcPagesYetToWrite = TargetPages; + + // Get the first shared cache map to start the scan + SharedMap = + CONTAINING_RECORD(CcLazyWriterCursor.SharedCacheMapLinks.Flink, SHARED_CACHE_MAP, SharedCacheMapLinks); + + while (SharedMap != FirstMap) + { + MapLinks = &SharedMap->SharedCacheMapLinks; + + if (MapLinks == &CcLazyWriterCursor.SharedCacheMapLinks) + break; + + if (!FirstMap) + FirstMap = SharedMap; + + // Check if we need to skip to the next map + if (IsGoToNextMap(SharedMap, TargetPages)) + { + counter++; + if (counter >= 20 && !(SharedMap->Flags & (0x20 | 0x800))) + { + SharedMap->DirtyPages++; + SharedMap->Flags |= 0x20; + KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); + + counter = 0; + + OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); + SharedMap->Flags &= ~0x20; + SharedMap->DirtyPages--; + } + + goto NextMap; + } + + SharedMap->PagesToWrite = SharedMap->DirtyPages; + + // Adjust pages to write based on certain conditions + if ((SharedMap->Flags & SHARE_FL_MODIFIED_NO_WRITE) && SharedMap->DirtyPages >= 0x40 && + CcCapturedSystemSize != MmSmallSystem) + { + SharedMap->PagesToWrite /= 8; + } + + if (!IsNoPagesToWrite) + { + if (TargetPages > SharedMap->PagesToWrite) + { + TargetPages -= SharedMap->PagesToWrite; + } + else if ((SharedMap->Flags & SHARE_FL_MODIFIED_NO_WRITE) || + (FirstMap == SharedMap && !(SharedMap->LazyWritePassCount & 0xF))) + { + TargetPages = 0; + IsNoPagesToWrite = TRUE; + + IsDoubleScan = TRUE; + } + else + { + RemoveEntryList(&CcLazyWriterCursor.SharedCacheMapLinks); + InsertTailList(MapLinks, &CcLazyWriterCursor.SharedCacheMapLinks); + + TargetPages = 0; + IsNoPagesToWrite = TRUE; + } + } + + SharedMap->Flags |= 0x20; + SharedMap->DirtyPages++; + + KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); + + // Acquire a work item for writing behind + Prcb = KeGetCurrentPrcb(); + LookasideList = Prcb->PPLookasideList[5].P; + LookasideList->TotalAllocates++; + WorkItem = (PWORK_QUEUE_ENTRY)InterlockedPopEntrySList(&LookasideList->ListHead); + + if (!WorkItem) + { + LookasideList->AllocateMisses++; + LookasideList = Prcb->PPLookasideList[5].L; + LookasideList->TotalAllocates++; + WorkItem = (PWORK_QUEUE_ENTRY)InterlockedPopEntrySList(&LookasideList->ListHead); + + if (!WorkItem) + { + LookasideList->AllocateMisses++; + WorkItem = LookasideList->Allocate(LookasideList->Type, LookasideList->Size, LookasideList->Tag); + } + } + + // Check if work item is available + if (!WorkItem) + { + OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); + SharedMap->Flags &= ~0x20; + SharedMap->DirtyPages--; + break; + } + + // Initialize work item for write behind function + WorkItem->Function = WriteBehind; + WorkItem->Parameters.Write.SharedCacheMap = SharedMap; + + OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock); + + SharedMap->DirtyPages--; + + // Post work item to appropriate queue + if (SharedMap->Flags & SHARE_FL_WAITING_TEARDOWN) + { + SharedMap->WriteBehindWorkQueueEntry = (PVOID)((ULONG_PTR)WorkItem | 1); + CcPostWorkQueue(WorkItem, &CcFastTeardownWorkQueue); + } + else + { + SharedMap->WriteBehindWorkQueueEntry = WorkItem; + CcPostWorkQueue(WorkItem, &CcRegularWorkQueue); + } + + counter = 0; + + NextMap: + + SharedMap = CONTAINING_RECORD(MapLinks->Flink, SHARED_CACHE_MAP, SharedCacheMapLinks); + + if (IsDoubleScan) + { + RemoveEntryList(&CcLazyWriterCursor.SharedCacheMapLinks); + InsertHeadList(MapLinks, &CcLazyWriterCursor.SharedCacheMapLinks); + + IsDoubleScan = FALSE; + } + } + + // Post remaining work items to regular work queue + while (!IsListEmpty(&PostWorkList)) + { + PWORK_QUEUE_ENTRY workItem; + PLIST_ENTRY entry; + + entry = RemoveHeadList(&PostWorkList); + workItem = CONTAINING_RECORD(entry, WORK_QUEUE_ENTRY, WorkQueueLinks); + + CcPostWorkQueue(workItem, &CcRegularWorkQueue); + } + CcComputeNextScanTime(&OldestLWSTimeStamp, &NextScanDelay); + + if (!IsListEmpty(&PostWorkList) || !IsListEmpty(&CcDeferredWrites) || MmRegistryStatus.ProductStatus || + NextScanDelay.QuadPart != 0x7FFFFFFFFFFFFFFF)) + { + /* Schedule a lazy write scan */ + CcRescheduleLazyWriteScan(&NextScanDelay); + /* If forced disable is set, clear it */ + if (CcForcedDisableLazywriteScan) + { + CcForcedDisableLazywriteScan = false; + } + } + else + { + /* Set forced disable and deactivate the scan */ + CcForcedDisableLazywriteScan = true; + LazyWriter.ScanActive = false; + } + } + + KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql); + + // Check if there are deferred writes pending + if (!IsListEmpty(&CcDeferredWrites)) + { + CcPostDeferredWrites(); + CcScheduleLazyWriteScan(FALSE); + } +}