[NTOSKRNL] Drop the always running thread for lazy writer.
authorPierre Schweitzer <pierre@reactos.org>
Wed, 7 Feb 2018 17:10:59 +0000 (18:10 +0100)
committerPierre Schweitzer <pierre@reactos.org>
Wed, 7 Feb 2018 20:37:17 +0000 (21:37 +0100)
Instead move to a threading model like the Windows one.
We'll queue several work items to be executed in a system thread (Cc worker)
when there are VACB that have been marked as dirty. Furthermore, some delay
will be observed before action to avoid killing the system with IOs.
This new threading model opens way for read ahead and write behind implementation.

Also, moved the initialization of the lazy writer to CcInitializeCacheManager()
it has nothing to do with views and shouldn't be initialized there.

Also, moved the lazy writer implementation to its own file.

Modified CcDeferWrite() and CcRosMarkDirtyVacb() to take into account the new threading model.

Introduced new functions:
- CcPostWorkQueue(): post an item to be handled by Cc worker and spawn a worker if required
- CcScanDpc(): called after some time (not to have lazy writer always running) to queue a lazy scan
- CcLazyWriteScan(): the lazy writer we used to have
- CcScheduleLazyWriteScan(): function to call when you want to start a lazy writer run. It will make a DPC after some time and queue execution
- CcWorkerThread(): the worker thread that will handle lazy write, read ahead, and so on

ntoskrnl/cache/newcc.h
ntoskrnl/cc/cacheman.c
ntoskrnl/cc/copy.c
ntoskrnl/cc/lazywrite.c [new file with mode: 0644]
ntoskrnl/cc/view.c
ntoskrnl/include/internal/cc.h
ntoskrnl/ntos.cmake

index 63f4841..adbf1c5 100644 (file)
@@ -53,7 +53,7 @@ CcMdlWriteComplete2(IN PFILE_OBJECT FileObject,
                     IN PLARGE_INTEGER FileOffset,
                     IN PMDL MdlChain);
 
-BOOLEAN
+VOID
 NTAPI
 CcInitView(VOID);
 
index 8303dd9..1718dde 100644 (file)
@@ -16,6 +16,9 @@
 
 BOOLEAN CcPfEnablePrefetcher;
 PFSN_PREFETCHER_GLOBALS CcPfGlobals;
+MM_SYSTEMSIZE CcCapturedSystemSize;
+
+static ULONG BugCheckFileId = 0x4 << 16;
 
 /* FUNCTIONS *****************************************************************/
 
@@ -42,15 +45,78 @@ NTAPI
 INIT_FUNCTION
 CcInitializeCacheManager(VOID)
 {
-    return CcInitView();
+    ULONG Thread;
+
+    CcInitView();
+
+    /* Initialize lazy-writer lists */
+    InitializeListHead(&CcIdleWorkerThreadList);
+    InitializeListHead(&CcRegularWorkQueue);
+
+    /* Define lazy writer threshold and the amount of workers,
+      * depending on the system type
+      */
+    CcCapturedSystemSize = MmQuerySystemSize();
+    switch (CcCapturedSystemSize)
+    {
+        case MmSmallSystem:
+            CcNumberWorkerThreads = ExCriticalWorkerThreads - 1;
+            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8;
+            break;
+
+        case MmMediumSystem:
+            CcNumberWorkerThreads = ExCriticalWorkerThreads - 1;
+            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 4;
+            break;
+
+        case MmLargeSystem:
+            CcNumberWorkerThreads = ExCriticalWorkerThreads - 2;
+            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8 + MmNumberOfPhysicalPages / 4;
+            break;
+
+        default:
+            CcNumberWorkerThreads = 1;
+            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8;
+            break;
+    }
+
+    /* Allocate a work item for all our threads */
+    for (Thread = 0; Thread < CcNumberWorkerThreads; ++Thread)
+    {
+        PWORK_QUEUE_ITEM Item;
+
+        Item = ExAllocatePoolWithTag(NonPagedPool, sizeof(WORK_QUEUE_ITEM), 'qWcC');
+        if (Item == NULL)
+        {
+            CcBugCheck(0, 0, 0);
+        }
+
+        /* By default, it's obviously idle */
+        ExInitializeWorkItem(Item, CcWorkerThread, Item);
+        InsertTailList(&CcIdleWorkerThreadList, &Item->List);
+    }
+
+    /* Initialize our lazy writer */
+    RtlZeroMemory(&LazyWriter, sizeof(LazyWriter));
+    InitializeListHead(&LazyWriter.WorkQueue);
+    /* Delay activation of the lazy writer */
+    KeInitializeDpc(&LazyWriter.ScanDpc, CcScanDpc, NULL);
+    KeInitializeTimer(&LazyWriter.ScanTimer);
+
+    /* Lookaside list for our work items */
+    ExInitializeNPagedLookasideList(&CcTwilightLookasideList, NULL, NULL, 0, sizeof(WORK_QUEUE_ENTRY), 'KWcC', 0);
+
+    /* HACK: for lazy writer watching */
+    KeInitializeEvent(&iLazyWriterNotify, NotificationEvent, FALSE);
+
+    return TRUE;
 }
 
 VOID
 NTAPI
 CcShutdownSystem(VOID)
 {
-    /* Inform the lazy writer it has to stop activity */
-    CcShutdownLazyWriter();
+    /* NOTHING TO DO */
 }
 
 /*
index 3d60a1a..194526c 100644 (file)
@@ -533,6 +533,12 @@ CcDeferWrite (
                                     &Context->DeferredWriteLinks,
                                     &CcDeferredWriteSpinLock);
     }
+
+    /* FIXME: lock master */
+    if (!LazyWriter.ScanActive)
+    {
+        CcScheduleLazyWriteScan(FALSE);
+    }
 }
 
 /*
diff --git a/ntoskrnl/cc/lazywrite.c b/ntoskrnl/cc/lazywrite.c
new file mode 100644 (file)
index 0000000..bda7ca2
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS kernel
+ * FILE:            ntoskrnl/cc/lazywrite.c
+ * PURPOSE:         Cache manager
+ *
+ * PROGRAMMERS:     Pierre Schweitzer (pierre@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <debug.h>
+
+typedef enum _WORK_QUEUE_FUNCTIONS
+{
+    ReadAhead = 1,
+    WriteBehind = 2,
+    LazyWrite = 3,
+    SetDone = 4,
+} WORK_QUEUE_FUNCTIONS, *PWORK_QUEUE_FUNCTIONS;
+
+/* Counters:
+ * - Amount of pages flushed by lazy writer
+ * - Number of times lazy writer ran
+ */
+ULONG CcLazyWritePages = 0;
+ULONG CcLazyWriteIos = 0;
+
+/* Internal vars (MS):
+ * - Lazy writer status structure
+ * - Lookaside list where to allocate work items
+ * - Queue for regular work items
+ * - Available worker threads
+ * - Marker for throttling queues
+ * - Number of ongoing workers
+ * - Three seconds delay for lazy writer 
+ * - One second delay for lazy writer
+ * - Zero delay for lazy writer
+ * - Number of worker threads
+ */
+LAZY_WRITER LazyWriter;
+NPAGED_LOOKASIDE_LIST CcTwilightLookasideList;
+LIST_ENTRY CcRegularWorkQueue;
+LIST_ENTRY CcIdleWorkerThreadList;
+BOOLEAN CcQueueThrottle = FALSE;
+ULONG CcNumberActiveWorkerThreads = 0;
+LARGE_INTEGER CcFirstDelay = RTL_CONSTANT_LARGE_INTEGER((LONGLONG)-1*3000*1000*10);
+LARGE_INTEGER CcIdleDelay = RTL_CONSTANT_LARGE_INTEGER((LONGLONG)-1*1000*1000*10);
+LARGE_INTEGER CcNoDelay = RTL_CONSTANT_LARGE_INTEGER((LONGLONG)0);
+ULONG CcNumberWorkerThreads;
+
+/* Internal vars (ROS):
+ */
+KEVENT iLazyWriterNotify;
+
+/* FUNCTIONS *****************************************************************/
+
+VOID
+CcPostWorkQueue(
+    IN PWORK_QUEUE_ENTRY WorkItem,
+    IN PLIST_ENTRY WorkQueue)
+{
+    KIRQL OldIrql;
+    PWORK_QUEUE_ITEM ThreadToSpawn;
+
+    /* First of all, insert the item in the queue */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueueWorkQueueLock);
+    InsertTailList(WorkQueue, &WorkItem->WorkQueueLinks);
+
+    /* Now, define whether we have to spawn a new work thread
+     * We will spawn a new one if:
+     * - There's no throttle in action
+     * - There's still at least one idle thread
+     */
+    ThreadToSpawn = NULL;
+    if (!CcQueueThrottle && !IsListEmpty(&CcIdleWorkerThreadList))
+    {
+        PLIST_ENTRY ListEntry;
+
+        /* Get the idle thread */
+        ListEntry = RemoveHeadList(&CcIdleWorkerThreadList);
+        ThreadToSpawn = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ITEM, List);
+
+        /* We're going to have one more! */
+        CcNumberActiveWorkerThreads += 1;
+    }
+
+    KeReleaseQueuedSpinLock(LockQueueWorkQueueLock, OldIrql);
+
+    /* If we have a thread to spawn, do it! */
+    if (ThreadToSpawn != NULL)
+    {
+        /* We NULLify it to be consistent with initialization */
+        ThreadToSpawn->List.Flink = NULL;
+        ExQueueWorkItem(ThreadToSpawn, CriticalWorkQueue);
+    }
+}
+
+VOID
+NTAPI
+CcScanDpc(
+    IN PKDPC Dpc,
+    IN PVOID DeferredContext,
+    IN PVOID SystemArgument1,
+    IN PVOID SystemArgument2)
+{
+    PWORK_QUEUE_ENTRY WorkItem;
+
+    /* Allocate a work item */
+    WorkItem = ExAllocateFromNPagedLookasideList(&CcTwilightLookasideList);
+    if (WorkItem == NULL)
+    {
+        LazyWriter.ScanActive = FALSE;
+        return;
+    }
+
+    /* And post it, it will be for lazy write */
+    WorkItem->Function = LazyWrite;
+    CcPostWorkQueue(WorkItem, &CcRegularWorkQueue);
+}
+
+/* FIXME: handle master lock */
+VOID
+CcLazyWriteScan(VOID)
+{
+    ULONG Target;
+    ULONG Count;
+    PLIST_ENTRY ListEntry;
+
+    /* We're not sleeping anymore */
+    KeClearEvent(&iLazyWriterNotify);
+
+    /* Our target is one-eighth of the dirty pages */
+    Target = CcTotalDirtyPages / 8;
+    if (Target != 0)
+    {
+        /* Flush! */
+        DPRINT1("Lazy writer starting (%d)\n", Target);
+        CcRosFlushDirtyPages(Target, &Count, FALSE, TRUE);
+
+        /* And update stats */
+        CcLazyWritePages += Count;
+        ++CcLazyWriteIos;
+        DPRINT1("Lazy writer done (%d)\n", Count);
+    }
+
+    /* Inform people waiting on us that we're done */
+    KeSetEvent(&iLazyWriterNotify, IO_DISK_INCREMENT, FALSE);
+
+    /* Likely not optimal, but let's handle one deferred write now! */
+    ListEntry = ExInterlockedRemoveHeadList(&CcDeferredWrites, &CcDeferredWriteSpinLock);
+    if (ListEntry != NULL)
+    {
+        PDEFERRED_WRITE Context;
+
+        /* Extract the context */
+        Context = CONTAINING_RECORD(ListEntry, DEFERRED_WRITE, DeferredWriteLinks);
+        ASSERT(Context->NodeTypeCode == NODE_TYPE_DEFERRED_WRITE);
+
+        /* Can we write now? */
+        if (CcCanIWrite(Context->FileObject, Context->BytesToWrite, FALSE, TRUE))
+        {
+            /* Yes! Do it, and destroy the associated context */
+            Context->PostRoutine(Context->Context1, Context->Context2);
+            ExFreePoolWithTag(Context, 'CcDw');
+        }
+        else
+        {
+            /* Otherwise, requeue it, but in tail, so that it doesn't block others
+             * This is clearly to improve, but given the poor algorithm used now
+             * It's better than nothing!
+             */
+            ExInterlockedInsertTailList(&CcDeferredWrites,
+                                        &Context->DeferredWriteLinks,
+                                        &CcDeferredWriteSpinLock);
+        }
+    }
+
+    /* We're no longer active */
+    LazyWriter.ScanActive = FALSE;
+}
+
+VOID CcScheduleLazyWriteScan(
+    IN BOOLEAN NoDelay)
+{
+    /* If no delay, immediately start lazy writer,
+     * no matter it was already started
+     */
+    if (NoDelay)
+    {
+        LazyWriter.ScanActive = TRUE;
+        KeSetTimer(&LazyWriter.ScanTimer, CcNoDelay, &LazyWriter.ScanDpc);
+    }
+    /* Otherwise, if it's not running, just wait three seconds to start it */
+    else if (!LazyWriter.ScanActive)
+    {
+        LazyWriter.ScanActive = TRUE;
+        KeSetTimer(&LazyWriter.ScanTimer, CcFirstDelay, &LazyWriter.ScanDpc);
+    }
+    /* Finally, already running, so queue for the next second */
+    else
+    {
+        KeSetTimer(&LazyWriter.ScanTimer, CcIdleDelay, &LazyWriter.ScanDpc);
+    }
+}
+
+VOID
+NTAPI
+CcWorkerThread(
+    IN PVOID Parameter)
+{
+    KIRQL OldIrql;
+    BOOLEAN DropThrottle;
+    PWORK_QUEUE_ITEM Item;
+
+    /* Get back our thread item */
+    Item = Parameter;
+    /* And by default, don't touch throttle */
+    DropThrottle = FALSE;
+
+    /* Loop till we have jobs */
+    while (TRUE)
+    {
+        PWORK_QUEUE_ENTRY WorkItem;
+
+        /* Lock queues */
+        OldIrql = KeAcquireQueuedSpinLock(LockQueueWorkQueueLock);
+
+        /* If we have to touch throttle, reset it now! */
+        if (DropThrottle)
+        {
+            CcQueueThrottle = FALSE;
+            DropThrottle = FALSE;
+        }
+
+        /* If no work to do, we're done */
+        if (IsListEmpty(&CcRegularWorkQueue))
+        {
+            break;
+        }
+
+        /* Get our work item, if someone is waiting for us to finish
+         * and we're not the only thread in queue
+         * then, quit running to let the others do
+         * and throttle so that noone starts till current activity is over
+         */
+        WorkItem = CONTAINING_RECORD(CcRegularWorkQueue.Flink, WORK_QUEUE_ENTRY, WorkQueueLinks);
+        if (WorkItem->Function == SetDone && CcNumberActiveWorkerThreads > 1)
+        {
+            CcQueueThrottle = TRUE;
+            break;
+        }
+
+        /* Otherwise, remove current entry */
+        RemoveEntryList(&WorkItem->WorkQueueLinks);
+        KeReleaseQueuedSpinLock(LockQueueWorkQueueLock, OldIrql);
+
+        /* And handle it */
+        switch (WorkItem->Function)
+        {
+            /* We only support lazy write now */
+            case LazyWrite:
+                CcLazyWriteScan();
+                break;
+
+            case SetDone:
+                KeSetEvent(WorkItem->Parameters.Event.Event, IO_NO_INCREMENT, FALSE);
+                DropThrottle = TRUE;
+                break;
+        }
+
+        /* And release the item */
+        ExFreeToNPagedLookasideList(&CcTwilightLookasideList, WorkItem);
+    }
+
+    /* Our thread is available again */
+    InsertTailList(&CcIdleWorkerThreadList, &Item->List);
+    /* One less worker */
+    --CcNumberActiveWorkerThreads;
+    KeReleaseQueuedSpinLock(LockQueueWorkQueueLock, OldIrql);
+}
index 99e7b1c..e050a33 100644 (file)
@@ -51,39 +51,22 @@ NPAGED_LOOKASIDE_LIST iBcbLookasideList;
 static NPAGED_LOOKASIDE_LIST SharedCacheMapLookasideList;
 static NPAGED_LOOKASIDE_LIST VacbLookasideList;
 
-/* Counters:
- * - Amount of pages flushed by lazy writer
- * - Number of times lazy writer ran
- */
-ULONG CcLazyWritePages = 0;
-ULONG CcLazyWriteIos = 0;
-
 /* Internal vars (MS):
  * - Threshold above which lazy writer will start action
  * - Amount of dirty pages
  * - List for deferred writes
  * - Spinlock when dealing with the deferred list
  * - List for "clean" shared cache maps
- * - One second delay for lazy writer
- * - System size when system started
- * - Number of worker threads
  */
 ULONG CcDirtyPageThreshold = 0;
 ULONG CcTotalDirtyPages = 0;
 LIST_ENTRY CcDeferredWrites;
 KSPIN_LOCK CcDeferredWriteSpinLock;
 LIST_ENTRY CcCleanSharedCacheMapList;
-LARGE_INTEGER CcIdleDelay = RTL_CONSTANT_LARGE_INTEGER((LONGLONG)-1*1000*1000*10);
-MM_SYSTEMSIZE CcCapturedSystemSize;
-ULONG CcNumberWorkerThreads;
 
 /* Internal vars (ROS):
- * - Event to notify lazy writer to shutdown
- * - Event to inform watchers lazy writer is done for this loop
  * - Lock for the CcCleanSharedCacheMapList list
  */
-KEVENT iLazyWriterShutdown;
-KEVENT iLazyWriterNotify;
 KSPIN_LOCK iSharedCacheMapLock;
 
 #if DBG
@@ -304,81 +287,6 @@ CcRosFlushDirtyPages (
     return STATUS_SUCCESS;
 }
 
-/* FIXME: Someday this could somewhat implement write-behind/read-ahead */
-VOID
-NTAPI
-CciLazyWriter(PVOID Unused)
-{
-    while (TRUE)
-    {
-        NTSTATUS Status;
-        PLIST_ENTRY ListEntry;
-        ULONG Target, Count = 0;
-
-        /* One per second or until we have to stop */
-        Status = KeWaitForSingleObject(&iLazyWriterShutdown,
-                                       Executive,
-                                       KernelMode,
-                                       FALSE,
-                                       &CcIdleDelay);
-
-        /* If we succeeed, we've to stop running! */
-        if (Status == STATUS_SUCCESS)
-        {
-            break;
-        }
-
-        /* We're not sleeping anymore */
-        KeClearEvent(&iLazyWriterNotify);
-
-        /* Our target is one-eighth of the dirty pages */
-        Target = CcTotalDirtyPages / 8;
-        if (Target != 0)
-        {
-            /* Flush! */
-            DPRINT("Lazy writer starting (%d)\n", Target);
-            CcRosFlushDirtyPages(Target, &Count, FALSE, TRUE);
-
-            /* And update stats */
-            CcLazyWritePages += Count;
-            ++CcLazyWriteIos;
-            DPRINT("Lazy writer done (%d)\n", Count);
-        }
-
-        /* Inform people waiting on us that we're done */
-        KeSetEvent(&iLazyWriterNotify, IO_DISK_INCREMENT, FALSE);
-
-        /* Likely not optimal, but let's handle one deferred write now! */
-        ListEntry = ExInterlockedRemoveHeadList(&CcDeferredWrites, &CcDeferredWriteSpinLock);
-        if (ListEntry != NULL)
-        {
-            PDEFERRED_WRITE Context;
-
-            /* Extract the context */
-            Context = CONTAINING_RECORD(ListEntry, DEFERRED_WRITE, DeferredWriteLinks);
-            ASSERT(Context->NodeTypeCode == NODE_TYPE_DEFERRED_WRITE);
-
-            /* Can we write now? */
-            if (CcCanIWrite(Context->FileObject, Context->BytesToWrite, FALSE, TRUE))
-            {
-                /* Yes! Do it, and destroy the associated context */
-                Context->PostRoutine(Context->Context1, Context->Context2);
-                ExFreePoolWithTag(Context, 'CcDw');
-            }
-            else
-            {
-                /* Otherwise, requeue it, but in tail, so that it doesn't block others
-                 * This is clearly to improve, but given the poor algorithm used now
-                 * It's better than nothing!
-                 */
-                ExInterlockedInsertTailList(&CcDeferredWrites,
-                                            &Context->DeferredWriteLinks,
-                                            &CcDeferredWriteSpinLock);
-            }
-        }
-    }
-}
-
 NTSTATUS
 CcRosTrimCache (
     ULONG Target,
@@ -611,6 +519,12 @@ CcRosMarkDirtyVacb (
 
     KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldIrql);
     KeReleaseGuardedMutex(&ViewLock);
+
+    /* FIXME: lock master */
+    if (!LazyWriter.ScanActive)
+    {
+        CcScheduleLazyWriteScan(FALSE);
+    }
 }
 
 VOID
@@ -1394,25 +1308,11 @@ CcGetFileObjectFromSectionPtrs (
 }
 
 VOID
-NTAPI
-CcShutdownLazyWriter (
-    VOID)
-{
-    /* Simply set the event, lazy writer will stop when it's done */
-    KeSetEvent(&iLazyWriterShutdown, IO_DISK_INCREMENT, FALSE);
-}
-
-BOOLEAN
 INIT_FUNCTION
 NTAPI
 CcInitView (
     VOID)
 {
-    HANDLE LazyWriter;
-    NTSTATUS Status;
-    KPRIORITY Priority;
-    OBJECT_ATTRIBUTES ObjectAttributes;
-
     DPRINT("CcInitView()\n");
 
     InitializeListHead(&DirtyVacbListHead);
@@ -1446,68 +1346,7 @@ CcInitView (
 
     MmInitializeMemoryConsumer(MC_CACHE, CcRosTrimCache);
 
-    /* Initialize lazy writer events */
-    KeInitializeEvent(&iLazyWriterShutdown, SynchronizationEvent, FALSE);
-    KeInitializeEvent(&iLazyWriterNotify, NotificationEvent, FALSE);
-
-    /* Define lazy writer threshold and the amount of workers,
-      * depending on the system type
-      */
-    CcCapturedSystemSize = MmQuerySystemSize();
-    switch (CcCapturedSystemSize)
-    {
-        case MmSmallSystem:
-            CcNumberWorkerThreads = ExCriticalWorkerThreads - 1;
-            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8;
-            break;
-
-        case MmMediumSystem:
-            CcNumberWorkerThreads = ExCriticalWorkerThreads - 1;
-            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 4;
-            break;
-
-        case MmLargeSystem:
-            CcNumberWorkerThreads = ExCriticalWorkerThreads - 2;
-            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8 + MmNumberOfPhysicalPages / 4;
-            break;
-
-        default:
-            CcNumberWorkerThreads = 1;
-            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8;
-            break;
-    }
-
-    /* Start the lazy writer thread */
-    InitializeObjectAttributes(&ObjectAttributes,
-                               NULL,
-                               OBJ_KERNEL_HANDLE,
-                               NULL,
-                               NULL);
-    Status = PsCreateSystemThread(&LazyWriter,
-                                  THREAD_ALL_ACCESS,
-                                  &ObjectAttributes,
-                                  NULL,
-                                  NULL,
-                                  CciLazyWriter,
-                                  NULL);
-    if (!NT_SUCCESS(Status))
-    {
-        return FALSE;
-    }
-
-    Priority = 27;
-    Status = NtSetInformationThread(LazyWriter,
-                                   ThreadPriority,
-                                   &Priority,
-                                   sizeof(Priority));
-    ASSERT(NT_SUCCESS(Status));
-
-    /* Handle is not needed */
-    ObCloseHandle(LazyWriter, KernelMode);
-
     CcInitCacheZeroPage();
-
-    return TRUE;
 }
 
 #if DBG && defined(KDBG)
index 3787563..18d34ef 100644 (file)
@@ -45,6 +45,11 @@ extern ULONG CcDirtyPageThreshold;
 extern ULONG CcTotalDirtyPages;
 extern LIST_ENTRY CcDeferredWrites;
 extern KSPIN_LOCK CcDeferredWriteSpinLock;
+extern ULONG CcNumberWorkerThreads;
+extern LIST_ENTRY CcIdleWorkerThreadList;
+extern LIST_ENTRY CcRegularWorkQueue;
+extern NPAGED_LOOKASIDE_LIST CcTwilightLookasideList;
+extern KEVENT iLazyWriterNotify;
 
 typedef struct _PF_SCENARIO_ID
 {
@@ -211,6 +216,43 @@ typedef struct _INTERNAL_BCB
     CSHORT RefCount; /* (At offset 0x34 on WinNT4) */
 } INTERNAL_BCB, *PINTERNAL_BCB;
 
+typedef struct _LAZY_WRITER
+{
+    LIST_ENTRY WorkQueue;
+    KDPC ScanDpc;
+    KTIMER ScanTimer;
+    BOOLEAN ScanActive;
+    BOOLEAN OtherWork;
+    BOOLEAN PendingTeardown;
+} LAZY_WRITER, *PLAZY_WRITER;
+
+typedef struct _WORK_QUEUE_ENTRY
+{
+    LIST_ENTRY WorkQueueLinks;
+    union
+    {
+        struct
+        {
+            FILE_OBJECT *FileObject;
+        } Read;
+        struct
+        {
+            SHARED_CACHE_MAP *SharedCacheMap;
+        } Write;
+        struct
+        {
+            KEVENT *Event;
+        } Event;
+        struct
+        {
+            unsigned long Reason;
+        } Notification;
+    } Parameters;
+    unsigned char Function;
+} WORK_QUEUE_ENTRY, *PWORK_QUEUE_ENTRY;
+
+extern LAZY_WRITER LazyWriter;
+
 #define NODE_TYPE_DEFERRED_WRITE 0x02FC
 
 VOID
@@ -249,7 +291,7 @@ CcRosGetVacb(
     PROS_VACB *Vacb
 );
 
-BOOLEAN
+VOID
 NTAPI
 CcInitView(VOID);
 
@@ -371,6 +413,21 @@ VOID
 NTAPI
 CcShutdownSystem(VOID);
 
+VOID
+NTAPI
+CcWorkerThread(PVOID Parameter);
+
+VOID
+NTAPI
+CcScanDpc(
+    PKDPC Dpc,
+    PVOID DeferredContext,
+    PVOID SystemArgument1,
+    PVOID SystemArgument2);
+
+VOID
+CcScheduleLazyWriteScan(BOOLEAN NoDelay);
+
 FORCEINLINE
 NTSTATUS
 CcRosAcquireVacbLock(
@@ -418,3 +475,5 @@ IsPointInRange(
 {
     return DoRangesIntersect(Offset1, Length1, Point, 1);
 }
+
+#define CcBugCheck(A, B, C) KeBugCheckEx(CACHE_MANAGER, BugCheckFileId | ((ULONG)(__LINE__)), A, B, C)
index ceb2876..94e0dc8 100644 (file)
@@ -33,6 +33,7 @@ else()
         ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/cacheman.c
         ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/copy.c
         ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/fs.c
+        ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/lazywrite.c
         ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/mdl.c
         ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/pin.c
         ${REACTOS_SOURCE_DIR}/ntoskrnl/cc/view.c)