[NTOSKRNL] Implement support for deferred writes in Cc.
authorPierre Schweitzer <pierre@reactos.org>
Tue, 23 Jan 2018 21:56:23 +0000 (22:56 +0100)
committerPierre Schweitzer <pierre@reactos.org>
Tue, 23 Jan 2018 22:25:26 +0000 (23:25 +0100)
Namely, implement CcCanIWrite() (very basic, and likely wrong).
And implement CcDeferWrite() which will queue the write operation.

In CciLazyWriter() (which may be renamed CcWorkerThread() ;-)),
handle the queued write operations one by one. This is likely
not to be accurate, but, given we have only on FS supporting
this for now (NFS / RDBSS / Shares), this is OK.

CORE-14235

ntoskrnl/cc/copy.c
ntoskrnl/cc/view.c
ntoskrnl/include/internal/cc.h

index dadae02..4c68bf0 100644 (file)
@@ -378,6 +378,22 @@ CcCanIWrite (
     CCTRACE(CC_API_DEBUG, "FileObject=%p BytesToWrite=%lu Wait=%d Retrying=%d\n",
         FileObject, BytesToWrite, Wait, Retrying);
 
+    /* We cannot write if dirty pages count is above threshold */
+    if (CcTotalDirtyPages > CcDirtyPageThreshold)
+    {
+        return FALSE;
+    }
+
+    /* We cannot write if dirty pages count will bring use above
+     * XXX: Might not be accurate
+     */
+    if (CcTotalDirtyPages + (BytesToWrite / PAGE_SIZE) > CcDirtyPageThreshold)
+    {
+        return FALSE;
+    }
+
+    /* FIXME: Handle per-file threshold */
+
     return TRUE;
 }
 
@@ -442,7 +458,7 @@ CcCopyWrite (
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 VOID
 NTAPI
@@ -454,10 +470,43 @@ CcDeferWrite (
     IN ULONG BytesToWrite,
     IN BOOLEAN Retrying)
 {
+    PROS_DEFERRED_WRITE_CONTEXT Context;
+
     CCTRACE(CC_API_DEBUG, "FileObject=%p PostRoutine=%p Context1=%p Context2=%p BytesToWrite=%lu Retrying=%d\n",
         FileObject, PostRoutine, Context1, Context2, BytesToWrite, Retrying);
 
-    PostRoutine(Context1, Context2);
+    /* Try to allocate a context for queueing the write operation */
+    Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(ROS_DEFERRED_WRITE_CONTEXT), 'CcDw');
+    /* If it failed, immediately execute the operation! */
+    if (Context == NULL)
+    {
+        PostRoutine(Context1, Context2);
+        return;
+    }
+
+    /* Otherwise, initialize the context */
+    Context->FileObject = FileObject;
+    Context->PostRoutine = PostRoutine;
+    Context->Context1 = Context1;
+    Context->Context2 = Context2;
+    Context->BytesToWrite = BytesToWrite;
+    Context->Retrying = Retrying;
+
+    /* And queue it */
+    if (Retrying)
+    {
+        /* To the top, if that's a retry */
+        ExInterlockedInsertHeadList(&CcDeferredWrites,
+                                    &Context->CcDeferredWritesEntry,
+                                    &CcDeferredWriteSpinLock);
+    }
+    else
+    {
+        /* To the bottom, if that's a first time */
+        ExInterlockedInsertTailList(&CcDeferredWrites,
+                                    &Context->CcDeferredWritesEntry,
+                                    &CcDeferredWriteSpinLock);
+    }
 }
 
 /*
index 465fffa..61036b0 100644 (file)
@@ -60,9 +60,13 @@ ULONG CcLazyWriteIos = 0;
 /* Internal vars (MS):
  * - Threshold above which lazy writer will start action
  * - Amount of dirty pages
+ * - List for deferred writes
+ * - Spinlock when dealing with the deferred list
  */
 ULONG CcDirtyPageThreshold = 0;
 ULONG CcTotalDirtyPages = 0;
+LIST_ENTRY CcDeferredWrites;
+KSPIN_LOCK CcDeferredWriteSpinLock;
 
 /* Internal vars (ROS):
  * - Event to notify lazy writer to shutdown
@@ -308,6 +312,7 @@ CciLazyWriter(PVOID Unused)
     while (TRUE)
     {
         NTSTATUS Status;
+        PLIST_ENTRY ListEntry;
         ULONG Target, Count = 0;
 
         /* One per second or until we have to stop */
@@ -342,6 +347,34 @@ CciLazyWriter(PVOID Unused)
 
         /* Inform people waiting on us that we're done */
         KeSetEvent(&iLazyWriterNotify, IO_DISK_INCREMENT, FALSE);
+
+        /* Likely not optimal, but let's handle one deferred write now! */
+        ListEntry = ExInterlockedRemoveHeadList(&CcDeferredWrites, &CcDeferredWriteSpinLock);
+        if (ListEntry != NULL)
+        {
+            PROS_DEFERRED_WRITE_CONTEXT Context;
+
+            /* Extract the context */
+            Context = CONTAINING_RECORD(ListEntry, ROS_DEFERRED_WRITE_CONTEXT, CcDeferredWritesEntry);
+
+            /* Can we write now? */
+            if (CcCanIWrite(Context->FileObject, Context->BytesToWrite, FALSE, Context->Retrying))
+            {
+                /* Yes! Do it, and destroy the associated context */
+                Context->PostRoutine(Context->Context1, Context->Context2);
+                ExFreePoolWithTag(Context, 'CcDw');
+            }
+            else
+            {
+                /* Otherwise, requeue it, but in tail, so that it doesn't block others
+                 * This is clearly to improve, but given the poor algorithm used now
+                 * It's better than nothing!
+                 */
+                ExInterlockedInsertTailList(&CcDeferredWrites,
+                                            &Context->CcDeferredWritesEntry,
+                                            &CcDeferredWriteSpinLock);
+            }
+        }
     }
 }
 
@@ -1358,6 +1391,8 @@ CcInitView (
 
     InitializeListHead(&DirtyVacbListHead);
     InitializeListHead(&VacbLruListHead);
+    InitializeListHead(&CcDeferredWrites);
+    KeInitializeSpinLock(&CcDeferredWriteSpinLock);
     KeInitializeGuardedMutex(&ViewLock);
     ExInitializeNPagedLookasideList(&iBcbLookasideList,
                                     NULL,
index cc62381..05eb53f 100644 (file)
 //
 extern ULONG CcRosTraceLevel;
 extern LIST_ENTRY DirtyVacbListHead;
+extern ULONG CcDirtyPageThreshold;
+extern ULONG CcTotalDirtyPages;
+extern LIST_ENTRY CcDeferredWrites;
+extern KSPIN_LOCK CcDeferredWriteSpinLock;
 
 typedef struct _PF_SCENARIO_ID
 {
@@ -193,6 +197,17 @@ typedef struct _ROS_VACB
     /* Pointer to the next VACB in a chain. */
 } ROS_VACB, *PROS_VACB;
 
+typedef struct _ROS_DEFERRED_WRITE_CONTEXT
+{
+    LIST_ENTRY CcDeferredWritesEntry;
+    PFILE_OBJECT FileObject;
+    PCC_POST_DEFERRED_WRITE PostRoutine;
+    PVOID Context1;
+    PVOID Context2;
+    ULONG BytesToWrite;
+    BOOLEAN Retrying;
+} ROS_DEFERRED_WRITE_CONTEXT, *PROS_DEFERRED_WRITE_CONTEXT;
+
 typedef struct _INTERNAL_BCB
 {
     /* Lock */