[NTOSKRNL] Add wait support in CcCanIWrite()
authorPierre Schweitzer <pierre@reactos.org>
Wed, 7 Feb 2018 20:09:36 +0000 (21:09 +0100)
committerPierre Schweitzer <pierre@reactos.org>
Wed, 7 Feb 2018 20:37:17 +0000 (21:37 +0100)
ntoskrnl/cc/copy.c
ntoskrnl/include/internal/cc.h

index a150848..30360b6 100644 (file)
@@ -446,6 +446,8 @@ CcCanIWrite (
     IN BOOLEAN Wait,
     IN BOOLEAN Retrying)
 {
+    KEVENT WaitEvent;
+    DEFERRED_WRITE Context;
     PFSRTL_COMMON_FCB_HEADER Fcb;
     PROS_SHARED_CACHE_MAP SharedCacheMap;
 
@@ -455,7 +457,8 @@ CcCanIWrite (
     /* We cannot write if dirty pages count is above threshold */
     if (CcTotalDirtyPages > CcDirtyPageThreshold)
     {
-        return FALSE;
+        /* Can the caller wait till it's possible to write? */
+        goto CanIWait;
     }
 
     /* We cannot write if dirty pages count will bring use above
@@ -463,7 +466,8 @@ CcCanIWrite (
      */
     if (CcTotalDirtyPages + (BytesToWrite / PAGE_SIZE) > CcDirtyPageThreshold)
     {
-        return FALSE;
+        /* Can the caller wait till it's possible to write? */
+        goto CanIWait;
     }
 
     /* Is there a limit per file object? */
@@ -479,17 +483,75 @@ CcCanIWrite (
     /* Is dirty page count above local threshold? */
     if (SharedCacheMap->DirtyPages > SharedCacheMap->DirtyPageThreshold)
     {
-        return FALSE;
+        /* Can the caller wait till it's possible to write? */
+        goto CanIWait;
     }
 
     /* We cannot write if dirty pages count will bring use above
      * XXX: Might not be accurate
      */
     if (SharedCacheMap->DirtyPages + (BytesToWrite / PAGE_SIZE) > SharedCacheMap->DirtyPageThreshold)
+    {
+        /* Can the caller wait till it's possible to write? */
+        goto CanIWait;
+    }
+
+    return TRUE;
+
+CanIWait:
+    /* If we reached that point, it means caller cannot write
+     * If he cannot wait, then fail and deny write
+     */
+    if (!Wait)
     {
         return FALSE;
     }
 
+    /* Otherwise, if there are no deferred writes yet, start the lazy writer */
+    if (IsListEmpty(&CcDeferredWrites))
+    {
+        KIRQL OldIrql;
+
+        OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+        CcScheduleLazyWriteScan(TRUE);
+        KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+    }
+
+    /* Initialize our wait event */
+    KeInitializeEvent(&WaitEvent, NotificationEvent, FALSE);
+
+    /* And prepare a dummy context */
+    Context.NodeTypeCode = NODE_TYPE_DEFERRED_WRITE;
+    Context.NodeByteSize = sizeof(DEFERRED_WRITE);
+    Context.FileObject = FileObject;
+    Context.BytesToWrite = BytesToWrite;
+    Context.LimitModifiedPages = BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES);
+    Context.Event = &WaitEvent;
+
+    /* And queue it */
+    if (Retrying)
+    {
+        /* To the top, if that's a retry */
+        ExInterlockedInsertHeadList(&CcDeferredWrites,
+                                    &Context.DeferredWriteLinks,
+                                    &CcDeferredWriteSpinLock);
+    }
+    else
+    {
+        /* To the bottom, if that's a first time */
+        ExInterlockedInsertTailList(&CcDeferredWrites,
+                                    &Context.DeferredWriteLinks,
+                                    &CcDeferredWriteSpinLock);
+    }
+
+    /* Now, we'll loop until our event is set. When it is set, it means that caller
+     * can immediately write, and has to
+     */
+    do
+    {
+        CcPostDeferredWrites();
+    } while (KeWaitForSingleObject(&WaitEvent, Executive, KernelMode, FALSE, &CcIdleDelay) != STATUS_SUCCESS);
+
     return TRUE;
 }
 
index 157b655..3a44fda 100644 (file)
@@ -50,6 +50,7 @@ extern LIST_ENTRY CcIdleWorkerThreadList;
 extern LIST_ENTRY CcRegularWorkQueue;
 extern LIST_ENTRY CcPostTickWorkQueue;
 extern NPAGED_LOOKASIDE_LIST CcTwilightLookasideList;
+extern LARGE_INTEGER CcIdleDelay;
 
 typedef struct _PF_SCENARIO_ID
 {