From: Pierre Schweitzer Date: Fri, 9 Feb 2018 12:56:16 +0000 (+0100) Subject: [NTOSKRNL] Rewrite CcCanIWrite() to make it more accurate and handle specific callers X-Git-Tag: 0.4.9-dev~46 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=945ff8ea2ee2abd48f5ecbe707131446546e75a3;ds=sidebyside [NTOSKRNL] Rewrite CcCanIWrite() to make it more accurate and handle specific callers --- diff --git a/ntoskrnl/cc/copy.c b/ntoskrnl/cc/copy.c index 4992ade2906..40897028fc0 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) {