[NTOSKRNL] Silence lazy writer now we know it works
[reactos.git] / ntoskrnl / cc / lazywrite.c
index bda7ca2..adb9fb0 100644 (file)
 #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
@@ -31,8 +23,10 @@ ULONG CcLazyWriteIos = 0;
 /* Internal vars (MS):
  * - Lazy writer status structure
  * - Lookaside list where to allocate work items
+ * - Queue for high priority work items (read ahead)
  * - Queue for regular work items
  * - Available worker threads
+ * - Queue for stuff to be queued after lazy writer is done
  * - Marker for throttling queues
  * - Number of ongoing workers
  * - Three seconds delay for lazy writer 
@@ -42,8 +36,10 @@ ULONG CcLazyWriteIos = 0;
  */
 LAZY_WRITER LazyWriter;
 NPAGED_LOOKASIDE_LIST CcTwilightLookasideList;
+LIST_ENTRY CcExpressWorkQueue;
 LIST_ENTRY CcRegularWorkQueue;
 LIST_ENTRY CcIdleWorkerThreadList;
+LIST_ENTRY CcPostTickWorkQueue;
 BOOLEAN CcQueueThrottle = FALSE;
 ULONG CcNumberActiveWorkerThreads = 0;
 LARGE_INTEGER CcFirstDelay = RTL_CONSTANT_LARGE_INTEGER((LONGLONG)-1*3000*1000*10);
@@ -51,10 +47,6 @@ 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
@@ -121,65 +113,63 @@ CcScanDpc(
     CcPostWorkQueue(WorkItem, &CcRegularWorkQueue);
 }
 
-/* FIXME: handle master lock */
 VOID
 CcLazyWriteScan(VOID)
 {
     ULONG Target;
     ULONG Count;
+    KIRQL OldIrql;
     PLIST_ENTRY ListEntry;
+    LIST_ENTRY ToPost;
+    PWORK_QUEUE_ENTRY WorkItem;
 
-    /* We're not sleeping anymore */
-    KeClearEvent(&iLazyWriterNotify);
+    /* Do we have entries to queue after we're done? */
+    InitializeListHead(&ToPost);
+    OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+    if (LazyWriter.OtherWork)
+    {
+        while (!IsListEmpty(&CcPostTickWorkQueue))
+        {
+            ListEntry = RemoveHeadList(&CcPostTickWorkQueue);
+            WorkItem = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ENTRY, WorkQueueLinks);
+            InsertTailList(&ToPost, &WorkItem->WorkQueueLinks);
+        }
+        LazyWriter.OtherWork = FALSE;
+    }
+    KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
 
     /* Our target is one-eighth of the dirty pages */
     Target = CcTotalDirtyPages / 8;
     if (Target != 0)
     {
         /* Flush! */
-        DPRINT1("Lazy writer starting (%d)\n", Target);
+        DPRINT("Lazy writer starting (%d)\n", Target);
         CcRosFlushDirtyPages(Target, &Count, FALSE, TRUE);
 
         /* And update stats */
         CcLazyWritePages += Count;
         ++CcLazyWriteIos;
-        DPRINT1("Lazy writer done (%d)\n", Count);
+        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)
+    /* If we have deferred writes, try them now! */
+    if (!IsListEmpty(&CcDeferredWrites))
     {
-        PDEFERRED_WRITE Context;
-
-        /* Extract the context */
-        Context = CONTAINING_RECORD(ListEntry, DEFERRED_WRITE, DeferredWriteLinks);
-        ASSERT(Context->NodeTypeCode == NODE_TYPE_DEFERRED_WRITE);
+        CcPostDeferredWrites();
+    }
 
-        /* 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);
-        }
+    /* Post items that were due for end of run */
+    while (!IsListEmpty(&ToPost))
+    {
+        ListEntry = RemoveHeadList(&ToPost);
+        WorkItem = CONTAINING_RECORD(ListEntry, WORK_QUEUE_ENTRY, WorkQueueLinks);
+        CcPostWorkQueue(WorkItem, &CcRegularWorkQueue);
     }
 
     /* We're no longer active */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
     LazyWriter.ScanActive = FALSE;
+    KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
 }
 
 VOID CcScheduleLazyWriteScan(
@@ -235,10 +225,22 @@ CcWorkerThread(
             DropThrottle = FALSE;
         }
 
-        /* If no work to do, we're done */
-        if (IsListEmpty(&CcRegularWorkQueue))
+        /* Check first if we have read ahead to do */
+        if (IsListEmpty(&CcExpressWorkQueue))
         {
-            break;
+            /* If not, check regular queue */
+            if (IsListEmpty(&CcRegularWorkQueue))
+            {
+                break;
+            }
+            else
+            {
+                WorkItem = CONTAINING_RECORD(CcRegularWorkQueue.Flink, WORK_QUEUE_ENTRY, WorkQueueLinks);
+            }
+        }
+        else
+        {
+            WorkItem = CONTAINING_RECORD(CcExpressWorkQueue.Flink, WORK_QUEUE_ENTRY, WorkQueueLinks);
         }
 
         /* Get our work item, if someone is waiting for us to finish
@@ -246,7 +248,6 @@ CcWorkerThread(
          * 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;
@@ -260,7 +261,10 @@ CcWorkerThread(
         /* And handle it */
         switch (WorkItem->Function)
         {
-            /* We only support lazy write now */
+            case ReadAhead:
+                CcPerformReadAhead(WorkItem->Parameters.Read.FileObject);
+                break;
+
             case LazyWrite:
                 CcLazyWriteScan();
                 break;
@@ -269,6 +273,10 @@ CcWorkerThread(
                 KeSetEvent(WorkItem->Parameters.Event.Event, IO_NO_INCREMENT, FALSE);
                 DropThrottle = TRUE;
                 break;
+
+            default:
+                DPRINT1("Ignored item: %p (%d)\n", WorkItem, WorkItem->Function);
+                break;
         }
 
         /* And release the item */
@@ -281,3 +289,47 @@ CcWorkerThread(
     --CcNumberActiveWorkerThreads;
     KeReleaseQueuedSpinLock(LockQueueWorkQueueLock, OldIrql);
 }
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+CcWaitForCurrentLazyWriterActivity (
+    VOID)
+{
+    KIRQL OldIrql;
+    KEVENT WaitEvent;
+    PWORK_QUEUE_ENTRY WorkItem;
+
+    /* Allocate a work item */
+    WorkItem = ExAllocateFromNPagedLookasideList(&CcTwilightLookasideList);
+    if (WorkItem == NULL)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* We want lazy writer to set our event */
+    WorkItem->Function = SetDone;
+    KeInitializeEvent(&WaitEvent, NotificationEvent, FALSE);
+    WorkItem->Parameters.Event.Event = &WaitEvent;
+
+    /* Use the post tick queue */
+    OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+    InsertTailList(&CcPostTickWorkQueue, &WorkItem->WorkQueueLinks);
+
+    /* Inform the lazy writer it will have to handle the post tick queue */
+    LazyWriter.OtherWork = TRUE;
+    /* And if it's not running, queue a lazy writer run
+     * And start it NOW, we want the response now
+     */
+    if (!LazyWriter.ScanActive)
+    {
+        CcScheduleLazyWriteScan(TRUE);
+    }
+
+    KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+
+    /* And now, wait until lazy writer replies */
+    return KeWaitForSingleObject(&WaitEvent, Executive, KernelMode, FALSE, NULL);
+}