[NTOSKRNL:CC] Implementation of Lazy Writer
Implemented following function *CcPostWorkQueue *CcScheduleLazyWriteScanEx *CcScheduleLazyWriteScan *CcExceptionFilter *CcPerformReadAhead *CcPostDeferredWrites *CcSetValidData *CcWriteBehind *CcLazyWriteScan
This commit is contained in:
parent
189cf42f74
commit
3ff9824c8b
862
NTOSKRNL/CC/cclazywriter.cpp
Normal file
862
NTOSKRNL/CC/cclazywriter.cpp
Normal file
@ -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 <ntoskrnl.h>
|
||||||
|
#define NTDEBUG
|
||||||
|
#include <debug.h>
|
||||||
|
#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<BOOL>(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<LONG *>(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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user