[NTOSKRNL] Don't let CcWriteVirtualAddress() deal with VACB dirty status
[reactos.git] / ntoskrnl / cc / copy.c
index 93b29c0..3d60a1a 100644 (file)
@@ -4,7 +4,8 @@
  * FILE:            ntoskrnl/cc/copy.c
  * PURPOSE:         Implements cache managers copy interface
  *
- * PROGRAMMERS:
+ * PROGRAMMERS:     Some people?
+ *                  Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* INCLUDES ******************************************************************/
@@ -26,6 +27,7 @@ typedef enum _CC_COPY_OPERATION
     CcOperationZero
 } CC_COPY_OPERATION;
 
+ULONG CcRosTraceLevel = 0;
 ULONG CcFastMdlReadWait;
 ULONG CcFastMdlReadNotPossible;
 ULONG CcFastReadNotPossible;
@@ -33,6 +35,8 @@ ULONG CcFastReadWait;
 ULONG CcFastReadNoWait;
 ULONG CcFastReadResourceMiss;
 
+extern KEVENT iLazyWriterNotify;
+
 /* FUNCTIONS *****************************************************************/
 
 VOID
@@ -64,7 +68,7 @@ NTAPI
 CcReadVirtualAddress (
     PROS_VACB Vacb)
 {
-    ULONG Size;
+    ULONG Size, Pages;
     PMDL Mdl;
     NTSTATUS Status;
     IO_STATUS_BLOCK IoStatus;
@@ -76,20 +80,38 @@ CcReadVirtualAddress (
         Size = VACB_MAPPING_GRANULARITY;
     }
 
-    Mdl = IoAllocateMdl(Vacb->BaseAddress, Size, FALSE, FALSE, NULL);
+    Pages = BYTES_TO_PAGES(Size);
+    ASSERT(Pages * PAGE_SIZE <= VACB_MAPPING_GRANULARITY);
+
+    Mdl = IoAllocateMdl(Vacb->BaseAddress, Pages * PAGE_SIZE, FALSE, FALSE, NULL);
     if (!Mdl)
     {
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-    MmBuildMdlForNonPagedPool(Mdl);
-    Mdl->MdlFlags |= MDL_IO_PAGE_READ;
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
-    Status = IoPageRead(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus);
-    if (Status == STATUS_PENDING)
+    Status = STATUS_SUCCESS;
+    _SEH2_TRY
+    {
+        MmProbeAndLockPages(Mdl, KernelMode, IoWriteAccess);
+    }
+    _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+        KeBugCheck(CACHE_MANAGER);
+    } _SEH2_END;
+
+    if (NT_SUCCESS(Status))
     {
-        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-        Status = IoStatus.Status;
+        Mdl->MdlFlags |= MDL_IO_PAGE_READ;
+        KeInitializeEvent(&Event, NotificationEvent, FALSE);
+        Status = IoPageRead(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus);
+        if (Status == STATUS_PENDING)
+        {
+            KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+            Status = IoStatus.Status;
+        }
+
+        MmUnlockPages(Mdl);
     }
 
     IoFreeMdl(Mdl);
@@ -120,7 +142,6 @@ CcWriteVirtualAddress (
     IO_STATUS_BLOCK IoStatus;
     KEVENT Event;
 
-    Vacb->Dirty = FALSE;
     Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart);
     if (Size > VACB_MAPPING_GRANULARITY)
     {
@@ -143,20 +164,34 @@ CcWriteVirtualAddress (
     {
         return STATUS_INSUFFICIENT_RESOURCES;
     }
-    MmBuildMdlForNonPagedPool(Mdl);
-    Mdl->MdlFlags |= MDL_IO_PAGE_READ;
-    KeInitializeEvent(&Event, NotificationEvent, FALSE);
-    Status = IoSynchronousPageWrite(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus);
-    if (Status == STATUS_PENDING)
+
+    Status = STATUS_SUCCESS;
+    _SEH2_TRY
     {
-        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
-        Status = IoStatus.Status;
+        MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess);
+    }
+    _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
+    {
+        Status = _SEH2_GetExceptionCode();
+        KeBugCheck(CACHE_MANAGER);
+    } _SEH2_END;
+
+    if (NT_SUCCESS(Status))
+    {
+        KeInitializeEvent(&Event, NotificationEvent, FALSE);
+        Status = IoSynchronousPageWrite(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus);
+        if (Status == STATUS_PENDING)
+        {
+            KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+            Status = IoStatus.Status;
+        }
+
+        MmUnlockPages(Mdl);
     }
     IoFreeMdl(Mdl);
     if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
     {
         DPRINT1("IoPageWrite failed, Status %x\n", Status);
-        Vacb->Dirty = TRUE;
         return Status;
     }
 
@@ -283,7 +318,7 @@ CcCopyData (
         CurrentOffset += PartialLength;
         BytesCopied += PartialLength;
 
-        if (Buffer)
+        if (Operation != CcOperationZero)
             Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength);
     }
 
@@ -320,7 +355,7 @@ CcCopyData (
         CurrentOffset += PartialLength;
         BytesCopied += PartialLength;
 
-        if (Buffer)
+        if (Operation != CcOperationZero)
             Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength);
     }
     IoStatus->Status = STATUS_SUCCESS;
@@ -339,8 +374,51 @@ CcCanIWrite (
     IN BOOLEAN Wait,
     IN BOOLEAN Retrying)
 {
-    UNIMPLEMENTED;
-    return FALSE;
+    PFSRTL_COMMON_FCB_HEADER Fcb;
+    PROS_SHARED_CACHE_MAP SharedCacheMap;
+
+    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;
+    }
+
+    /* Is there a limit per file object? */
+    Fcb = FileObject->FsContext;
+    SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+    if (!BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES) ||
+        SharedCacheMap->DirtyPageThreshold == 0)
+    {
+        /* Nope, so that's fine, allow write operation */
+        return TRUE;
+    }
+
+    /* Is dirty page count above local threshold? */
+    if (SharedCacheMap->DirtyPages > SharedCacheMap->DirtyPageThreshold)
+    {
+        return FALSE;
+    }
+
+    /* We cannot write if dirty pages count will bring use above
+     * XXX: Might not be accurate
+     */
+    if (SharedCacheMap->DirtyPages + (BytesToWrite / PAGE_SIZE) > SharedCacheMap->DirtyPageThreshold)
+    {
+        return FALSE;
+    }
+
+    return TRUE;
 }
 
 /*
@@ -356,6 +434,9 @@ CcCopyRead (
     OUT PVOID Buffer,
     OUT PIO_STATUS_BLOCK IoStatus)
 {
+    CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d\n",
+        FileObject, FileOffset->QuadPart, Length, Wait);
+
     DPRINT("CcCopyRead(FileObject 0x%p, FileOffset %I64x, "
            "Length %lu, Wait %u, Buffer 0x%p, IoStatus 0x%p)\n",
            FileObject, FileOffset->QuadPart, Length, Wait,
@@ -384,6 +465,9 @@ CcCopyWrite (
 {
     IO_STATUS_BLOCK IoStatus;
 
+    CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%I64d Length=%lu Wait=%d Buffer=%p\n",
+        FileObject, FileOffset->QuadPart, Length, Wait, Buffer);
+
     DPRINT("CcCopyWrite(FileObject 0x%p, FileOffset %I64x, "
            "Length %lu, Wait %u, Buffer 0x%p)\n",
            FileObject, FileOffset->QuadPart, Length, Wait, Buffer);
@@ -398,7 +482,7 @@ CcCopyWrite (
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 VOID
 NTAPI
@@ -410,7 +494,45 @@ CcDeferWrite (
     IN ULONG BytesToWrite,
     IN BOOLEAN Retrying)
 {
-    UNIMPLEMENTED;
+    PDEFERRED_WRITE Context;
+
+    CCTRACE(CC_API_DEBUG, "FileObject=%p PostRoutine=%p Context1=%p Context2=%p BytesToWrite=%lu Retrying=%d\n",
+        FileObject, PostRoutine, Context1, Context2, BytesToWrite, Retrying);
+
+    /* Try to allocate a context for queueing the write operation */
+    Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(DEFERRED_WRITE), 'CcDw');
+    /* If it failed, immediately execute the operation! */
+    if (Context == NULL)
+    {
+        PostRoutine(Context1, Context2);
+        return;
+    }
+
+    /* Otherwise, initialize the context */
+    RtlZeroMemory(Context, sizeof(DEFERRED_WRITE));
+    Context->NodeTypeCode = NODE_TYPE_DEFERRED_WRITE;
+    Context->NodeByteSize = sizeof(DEFERRED_WRITE);
+    Context->FileObject = FileObject;
+    Context->PostRoutine = PostRoutine;
+    Context->Context1 = Context1;
+    Context->Context2 = Context2;
+    Context->BytesToWrite = BytesToWrite;
+
+    /* 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);
+    }
 }
 
 /*
@@ -426,8 +548,24 @@ CcFastCopyRead (
     OUT PVOID Buffer,
     OUT PIO_STATUS_BLOCK IoStatus)
 {
-    UNIMPLEMENTED;
+    LARGE_INTEGER LargeFileOffset;
+    BOOLEAN Success;
+
+    CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu PageCount=%lu Buffer=%p\n",
+        FileObject, FileOffset, Length, PageCount, Buffer);
+
+    DBG_UNREFERENCED_PARAMETER(PageCount);
+
+    LargeFileOffset.QuadPart = FileOffset;
+    Success = CcCopyRead(FileObject,
+                         &LargeFileOffset,
+                         Length,
+                         TRUE,
+                         Buffer,
+                         IoStatus);
+    ASSERT(Success == TRUE);
 }
+
 /*
  * @unimplemented
  */
@@ -439,19 +577,43 @@ CcFastCopyWrite (
     IN ULONG Length,
     IN PVOID Buffer)
 {
-    UNIMPLEMENTED;
+    LARGE_INTEGER LargeFileOffset;
+    BOOLEAN Success;
+
+    CCTRACE(CC_API_DEBUG, "FileObject=%p FileOffset=%lu Length=%lu Buffer=%p\n",
+        FileObject, FileOffset, Length, Buffer);
+
+    LargeFileOffset.QuadPart = FileOffset;
+    Success = CcCopyWrite(FileObject,
+                          &LargeFileOffset,
+                          Length,
+                          TRUE,
+                          Buffer);
+    ASSERT(Success == TRUE);
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 NTSTATUS
 NTAPI
 CcWaitForCurrentLazyWriterActivity (
     VOID)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+
+    /* Lazy writer is done when its event is set */
+    Status = KeWaitForSingleObject(&iLazyWriterNotify,
+                                   Executive,
+                                   KernelMode,
+                                   FALSE,
+                                   NULL);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    return STATUS_SUCCESS;
 }
 
 /*
@@ -474,6 +636,9 @@ CcZeroData (
     IO_STATUS_BLOCK Iosb;
     KEVENT Event;
 
+    CCTRACE(CC_API_DEBUG, "FileObject=%p StartOffset=%I64u EndOffset=%I64u Wait=%d\n",
+        FileObject, StartOffset->QuadPart, EndOffset->QuadPart, Wait);
+
     DPRINT("CcZeroData(FileObject 0x%p, StartOffset %I64x, EndOffset %I64x, "
            "Wait %u)\n", FileObject, StartOffset->QuadPart, EndOffset->QuadPart,
            Wait);