[NTOSKRNL] Implement write behind in Cc
[reactos.git] / ntoskrnl / cc / lazywrite.c
index adb9fb0..275507e 100644 (file)
@@ -109,15 +109,33 @@ CcScanDpc(
     }
 
     /* And post it, it will be for lazy write */
-    WorkItem->Function = LazyWrite;
+    WorkItem->Function = LazyScan;
     CcPostWorkQueue(WorkItem, &CcRegularWorkQueue);
 }
 
+VOID
+CcWriteBehind(VOID)
+{
+    ULONG Target, Count;
+
+    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);
+    }
+}
+
 VOID
 CcLazyWriteScan(VOID)
 {
     ULONG Target;
-    ULONG Count;
     KIRQL OldIrql;
     PLIST_ENTRY ListEntry;
     LIST_ENTRY ToPost;
@@ -142,20 +160,15 @@ CcLazyWriteScan(VOID)
     Target = CcTotalDirtyPages / 8;
     if (Target != 0)
     {
-        /* Flush! */
-        DPRINT("Lazy writer starting (%d)\n", Target);
-        CcRosFlushDirtyPages(Target, &Count, FALSE, TRUE);
+        /* There is stuff to flush, schedule a write-behind operation */
 
-        /* And update stats */
-        CcLazyWritePages += Count;
-        ++CcLazyWriteIos;
-        DPRINT("Lazy writer done (%d)\n", Count);
-    }
-
-    /* If we have deferred writes, try them now! */
-    if (!IsListEmpty(&CcDeferredWrites))
-    {
-        CcPostDeferredWrites();
+        /* Allocate a work item */
+        WorkItem = ExAllocateFromNPagedLookasideList(&CcTwilightLookasideList);
+        if (WorkItem != NULL)
+        {
+            WorkItem->Function = WriteBehind;
+            CcPostWorkQueue(WorkItem, &CcRegularWorkQueue);
+        }
     }
 
     /* Post items that were due for end of run */
@@ -166,10 +179,22 @@ CcLazyWriteScan(VOID)
         CcPostWorkQueue(WorkItem, &CcRegularWorkQueue);
     }
 
-    /* We're no longer active */
-    OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
-    LazyWriter.ScanActive = FALSE;
-    KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+    /* If we have deferred writes, try them now! */
+    if (!IsListEmpty(&CcDeferredWrites))
+    {
+        CcPostDeferredWrites();
+        /* Reschedule immediately a lazy writer run
+         * Keep us active to have short idle delay
+         */
+        CcScheduleLazyWriteScan(FALSE);
+    }
+    else
+    {
+        /* We're no longer active */
+        OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+        LazyWriter.ScanActive = FALSE;
+        KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+    }
 }
 
 VOID CcScheduleLazyWriteScan(
@@ -202,13 +227,29 @@ CcWorkerThread(
     IN PVOID Parameter)
 {
     KIRQL OldIrql;
-    BOOLEAN DropThrottle;
+    BOOLEAN DropThrottle, WritePerformed;
     PWORK_QUEUE_ITEM Item;
+#if DBG
+    PIRP TopLevel;
+#endif
 
     /* Get back our thread item */
     Item = Parameter;
     /* And by default, don't touch throttle */
     DropThrottle = FALSE;
+    /* No write performed */
+    WritePerformed =  FALSE;
+
+#if DBG
+    /* Top level IRP should be clean when started
+     * Save it to catch buggy drivers (or bugs!)
+     */
+    TopLevel = IoGetTopLevelIrp();
+    if (TopLevel != NULL)
+    {
+        DPRINT1("(%p) TopLevel IRP for this thread: %p\n", PsGetCurrentThread(), TopLevel);
+    }
+#endif
 
     /* Loop till we have jobs */
     while (TRUE)
@@ -265,7 +306,12 @@ CcWorkerThread(
                 CcPerformReadAhead(WorkItem->Parameters.Read.FileObject);
                 break;
 
-            case LazyWrite:
+            case WriteBehind:
+                CcWriteBehind();
+                WritePerformed = TRUE;
+                break;
+
+            case LazyScan:
                 CcLazyWriteScan();
                 break;
 
@@ -288,6 +334,27 @@ CcWorkerThread(
     /* One less worker */
     --CcNumberActiveWorkerThreads;
     KeReleaseQueuedSpinLock(LockQueueWorkQueueLock, OldIrql);
+
+    /* If there are pending write openations and we have at least 20 dirty pages */
+    if (!IsListEmpty(&CcDeferredWrites) && CcTotalDirtyPages >= 20)
+    {
+        /* And if we performed a write operation previously, then
+         * stress the system a bit and reschedule a scan to find
+         * stuff to write
+         */
+        if (WritePerformed)
+        {
+            CcLazyWriteScan();
+        }
+    }
+
+#if DBG
+    /* Top level shouldn't have changed */
+    if (TopLevel != IoGetTopLevelIrp())
+    {
+        DPRINT1("(%p) Mismatching TopLevel: %p, %p\n", PsGetCurrentThread(), TopLevel, IoGetTopLevelIrp());
+    }
+#endif
 }
 
 /*