https://git.reactos.org/?p=reactos.git;a=commitdiff;h=c11e947f6135ee99bd046…
commit c11e947f6135ee99bd046c729b0ea81d714be2c9
Author: Pierre Schweitzer <pierre(a)reactos.org>
AuthorDate: Wed Feb 7 21:09:36 2018 +0100
Commit: Pierre Schweitzer <pierre(a)reactos.org>
CommitDate: Wed Feb 7 21:37:17 2018 +0100
[NTOSKRNL] Add wait support in CcCanIWrite()
---
ntoskrnl/cc/copy.c | 68 ++++++++++++++++++++++++++++++++++++++++--
ntoskrnl/include/internal/cc.h | 1 +
2 files changed, 66 insertions(+), 3 deletions(-)
diff --git a/ntoskrnl/cc/copy.c b/ntoskrnl/cc/copy.c
index a150848bb2..30360b6c31 100644
--- a/ntoskrnl/cc/copy.c
+++ b/ntoskrnl/cc/copy.c
@@ -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;
}
diff --git a/ntoskrnl/include/internal/cc.h b/ntoskrnl/include/internal/cc.h
index 157b655b3a..3a44fda908 100644
--- a/ntoskrnl/include/internal/cc.h
+++ b/ntoskrnl/include/internal/cc.h
@@ -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
{