https://git.reactos.org/?p=reactos.git;a=commitdiff;h=945ff8ea2ee2abd48f5ec…
commit 945ff8ea2ee2abd48f5ecbe707131446546e75a3
Author: Pierre Schweitzer <pierre(a)reactos.org>
AuthorDate: Fri Feb 9 13:56:16 2018 +0100
Commit: Pierre Schweitzer <pierre(a)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)
{