https://git.reactos.org/?p=reactos.git;a=commitdiff;h=945ff8ea2ee2abd48f5ecbe707131446546e75a3

commit 945ff8ea2ee2abd48f5ecbe707131446546e75a3
Author:     Pierre Schweitzer <pie...@reactos.org>
AuthorDate: Fri Feb 9 13:56:16 2018 +0100
Commit:     Pierre Schweitzer <pie...@reactos.org>
CommitDate: Fri Feb 9 13:56:16 2018 +0100

    [NTOSKRNL] Rewrite CcCanIWrite() to make it more accurate and handle 
specific callers
---
 ntoskrnl/cc/copy.c | 99 ++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 66 insertions(+), 33 deletions(-)

diff --git a/ntoskrnl/cc/copy.c b/ntoskrnl/cc/copy.c
index 4992ade290..40897028fc 100644
--- a/ntoskrnl/cc/copy.c
+++ b/ntoskrnl/cc/copy.c
@@ -27,6 +27,14 @@ typedef enum _CC_COPY_OPERATION
     CcOperationZero
 } CC_COPY_OPERATION;
 
+typedef enum _CC_CAN_WRITE_RETRY
+{
+    FirstTry = 0,
+    RetryAllowRemote = 253,
+    RetryForceCheckPerFile = 254,
+    RetryMasterLocked = 255,
+} CC_CAN_WRITE_RETRY;
+
 ULONG CcRosTraceLevel = 0;
 ULONG CcFastMdlReadWait;
 ULONG CcFastMdlReadNotPossible;
@@ -414,7 +422,7 @@ CcPostDeferredWrites(VOID)
             }
 
             /* Check we can write */
-            if (CcCanIWrite(DeferredWrite->FileObject, WrittenBytes, FALSE, 
TRUE))
+            if (CcCanIWrite(DeferredWrite->FileObject, WrittenBytes, FALSE, 
RetryForceCheckPerFile))
             {
                 /* We can, so remove it from the list and stop looking for 
entry */
                 RemoveEntryList(&DeferredWrite->DeferredWriteLinks);
@@ -614,9 +622,13 @@ CcCanIWrite (
     IN BOOLEAN Wait,
     IN BOOLEAN Retrying)
 {
+    KIRQL OldIrql;
     KEVENT WaitEvent;
+    ULONG Length, Pages;
+    BOOLEAN PerFileDefer;
     DEFERRED_WRITE Context;
     PFSRTL_COMMON_FCB_HEADER Fcb;
+    CC_CAN_WRITE_RETRY TryContext;
     PROS_SHARED_CACHE_MAP SharedCacheMap;
 
     CCTRACE(CC_API_DEBUG, "FileObject=%p BytesToWrite=%lu Wait=%d 
Retrying=%d\n",
@@ -628,53 +640,74 @@ CcCanIWrite (
         return TRUE;
     }
 
-    /* We cannot write if dirty pages count is above threshold */
-    if (CcTotalDirtyPages > CcDirtyPageThreshold)
+    TryContext = Retrying;
+    /* Allow remote file if not from posted */
+    if (IoIsFileOriginRemote(FileObject) && TryContext < RetryAllowRemote)
     {
-        /* Can the caller wait till it's possible to write? */
-        goto CanIWait;
+        return TRUE;
     }
 
-    /* We cannot write if dirty pages count will bring use above
-     * XXX: Might not be accurate
-     */
-    if (CcTotalDirtyPages + (BytesToWrite / PAGE_SIZE) > CcDirtyPageThreshold)
+    /* Don't exceed max tolerated size */
+    Length = MAX_ZERO_LENGTH;
+    if (BytesToWrite < MAX_ZERO_LENGTH)
     {
-        /* Can the caller wait till it's possible to write? */
-        goto CanIWait;
+        Length = BytesToWrite;
     }
 
-    /* Is there a limit per file object? */
+    /* Convert it to pages count */
+    Pages = (Length + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+    /* By default, assume limits per file won't be hit */
+    PerFileDefer = FALSE;
     Fcb = FileObject->FsContext;
-    SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
-    if (!BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES) ||
-        SharedCacheMap->DirtyPageThreshold == 0)
+    /* Do we have to check for limits per file? */
+    if (TryContext >= RetryForceCheckPerFile ||
+        BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES))
     {
-        /* Nope, so that's fine, allow write operation */
-        return TRUE;
-    }
+        /* If master is not locked, lock it now */
+        if (TryContext != RetryMasterLocked)
+        {
+            OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+        }
 
-    /* Is dirty page count above local threshold? */
-    if (SharedCacheMap->DirtyPages > SharedCacheMap->DirtyPageThreshold)
-    {
-        /* Can the caller wait till it's possible to write? */
-        goto CanIWait;
+        /* Let's not assume the file is cached... */
+        if (FileObject->SectionObjectPointer != NULL &&
+            FileObject->SectionObjectPointer->SharedCacheMap != NULL)
+        {
+            SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+            /* Do we have limits per file set? */
+            if (SharedCacheMap->DirtyPageThreshold != 0 &&
+                SharedCacheMap->DirtyPages != 0)
+            {
+                /* Yes, check whether they are blocking */
+                if (Pages + SharedCacheMap->DirtyPages > 
SharedCacheMap->DirtyPageThreshold)
+                {
+                    PerFileDefer = TRUE;
+                }
+            }
+        }
+
+        /* And don't forget to release master */
+        if (TryContext != RetryMasterLocked)
+        {
+            KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
+        }
     }
 
-    /* We cannot write if dirty pages count will bring use above
-     * XXX: Might not be accurate
+    /* So, now allow write if:
+     * - Not the first try or we have no throttling yet
+     * AND:
+     * - We don't execeed threshold!
      */
-    if (SharedCacheMap->DirtyPages + (BytesToWrite / PAGE_SIZE) > 
SharedCacheMap->DirtyPageThreshold)
+    if ((TryContext != FirstTry || IsListEmpty(&CcDeferredWrites)) &&
+        CcTotalDirtyPages + Pages < CcDirtyPageThreshold &&
+        !PerFileDefer)
     {
-        /* Can the caller wait till it's possible to write? */
-        goto CanIWait;
+        return TRUE;
     }
 
-    return TRUE;
-
-CanIWait:
-    /* If we reached that point, it means caller cannot write
-     * If he cannot wait, then fail and deny write
+    /* If we can wait, we'll start the wait loop for waiting till we can
+     * write for real
      */
     if (!Wait)
     {

Reply via email to