[NTOS:CC] Use UNIMPLEMENTED_ONCE instead of custom code
[reactos.git] / ntoskrnl / cc / cacheman.c
index 3eefc92..8ccd6a7 100644 (file)
@@ -5,6 +5,7 @@
  * PURPOSE:         Cache manager
  *
  * PROGRAMMERS:     David Welch (welch@cwcom.net)
+ *                  Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* INCLUDES *****************************************************************/
 #define NDEBUG
 #include <debug.h>
 
+BOOLEAN CcPfEnablePrefetcher;
 PFSN_PREFETCHER_GLOBALS CcPfGlobals;
+MM_SYSTEMSIZE CcCapturedSystemSize;
+
+static ULONG BugCheckFileId = 0x4 << 16;
 
 /* FUNCTIONS *****************************************************************/
 
@@ -40,10 +45,79 @@ NTAPI
 INIT_FUNCTION
 CcInitializeCacheManager(VOID)
 {
+    ULONG Thread;
+
     CcInitView();
+
+    /* Initialize lazy-writer lists */
+    InitializeListHead(&CcIdleWorkerThreadList);
+    InitializeListHead(&CcExpressWorkQueue);
+    InitializeListHead(&CcRegularWorkQueue);
+    InitializeListHead(&CcPostTickWorkQueue);
+
+    /* Define lazy writer threshold and the amount of workers,
+      * depending on the system type
+      */
+    CcCapturedSystemSize = MmQuerySystemSize();
+    switch (CcCapturedSystemSize)
+    {
+        case MmSmallSystem:
+            CcNumberWorkerThreads = ExCriticalWorkerThreads - 1;
+            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8;
+            break;
+
+        case MmMediumSystem:
+            CcNumberWorkerThreads = ExCriticalWorkerThreads - 1;
+            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 4;
+            break;
+
+        case MmLargeSystem:
+            CcNumberWorkerThreads = ExCriticalWorkerThreads - 2;
+            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8 + MmNumberOfPhysicalPages / 4;
+            break;
+
+        default:
+            CcNumberWorkerThreads = 1;
+            CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8;
+            break;
+    }
+
+    /* Allocate a work item for all our threads */
+    for (Thread = 0; Thread < CcNumberWorkerThreads; ++Thread)
+    {
+        PWORK_QUEUE_ITEM Item;
+
+        Item = ExAllocatePoolWithTag(NonPagedPool, sizeof(WORK_QUEUE_ITEM), 'qWcC');
+        if (Item == NULL)
+        {
+            CcBugCheck(0, 0, 0);
+        }
+
+        /* By default, it's obviously idle */
+        ExInitializeWorkItem(Item, CcWorkerThread, Item);
+        InsertTailList(&CcIdleWorkerThreadList, &Item->List);
+    }
+
+    /* Initialize our lazy writer */
+    RtlZeroMemory(&LazyWriter, sizeof(LazyWriter));
+    InitializeListHead(&LazyWriter.WorkQueue);
+    /* Delay activation of the lazy writer */
+    KeInitializeDpc(&LazyWriter.ScanDpc, CcScanDpc, NULL);
+    KeInitializeTimer(&LazyWriter.ScanTimer);
+
+    /* Lookaside list for our work items */
+    ExInitializeNPagedLookasideList(&CcTwilightLookasideList, NULL, NULL, 0, sizeof(WORK_QUEUE_ENTRY), 'KWcC', 0);
+
     return TRUE;
 }
 
+VOID
+NTAPI
+CcShutdownSystem(VOID)
+{
+    /* NOTHING TO DO */
+}
+
 /*
  * @unimplemented
  */
@@ -87,11 +161,103 @@ CcScheduleReadAhead (
        IN      ULONG                   Length
        )
 {
-       UNIMPLEMENTED;
+    KIRQL OldIrql;
+    LARGE_INTEGER NewOffset;
+    PROS_SHARED_CACHE_MAP SharedCacheMap;
+    PPRIVATE_CACHE_MAP PrivateCacheMap;
+
+    SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+    PrivateCacheMap = FileObject->PrivateCacheMap;
+
+    /* If file isn't cached, or if read ahead is disabled, this is no op */
+    if (SharedCacheMap == NULL || PrivateCacheMap == NULL ||
+        BooleanFlagOn(SharedCacheMap->Flags, READAHEAD_DISABLED))
+    {
+        return;
+    }
+
+    /* Round read length with read ahead mask */
+    Length = ROUND_UP(Length, PrivateCacheMap->ReadAheadMask + 1);
+    /* Compute the offset we'll reach */
+    NewOffset.QuadPart = FileOffset->QuadPart + Length;
+
+    /* Lock read ahead spin lock */
+    KeAcquireSpinLock(&PrivateCacheMap->ReadAheadSpinLock, &OldIrql);
+    /* Easy case: the file is sequentially read */
+    if (BooleanFlagOn(FileObject->Flags, FO_SEQUENTIAL_ONLY))
+    {
+        /* If we went backward, this is no go! */
+        if (NewOffset.QuadPart < PrivateCacheMap->ReadAheadOffset[1].QuadPart)
+        {
+            KeReleaseSpinLock(&PrivateCacheMap->ReadAheadSpinLock, OldIrql);
+            return;
+        }
+
+        /* FIXME: hackish, but will do the job for now */
+        PrivateCacheMap->ReadAheadOffset[1].QuadPart = NewOffset.QuadPart;
+        PrivateCacheMap->ReadAheadLength[1] = Length;
+    }
+    /* Other cases: try to find some logic in that mess... */
+    else
+    {
+        /* Let's check if we always read the same way (like going down in the file)
+         * and pretend it's enough for now
+         */
+        if (PrivateCacheMap->FileOffset2.QuadPart >= PrivateCacheMap->FileOffset1.QuadPart &&
+            FileOffset->QuadPart >= PrivateCacheMap->FileOffset2.QuadPart)
+        {
+            /* FIXME: hackish, but will do the job for now */
+            PrivateCacheMap->ReadAheadOffset[1].QuadPart = NewOffset.QuadPart;
+            PrivateCacheMap->ReadAheadLength[1] = Length;
+        }
+        else
+        {
+            /* FIXME: handle the other cases */
+            KeReleaseSpinLock(&PrivateCacheMap->ReadAheadSpinLock, OldIrql);
+            UNIMPLEMENTED_ONCE;
+            return;
+        }
+    }
+
+    /* If read ahead isn't active yet */
+    if (!PrivateCacheMap->Flags.ReadAheadActive)
+    {
+        PWORK_QUEUE_ENTRY WorkItem;
+
+        /* It's active now!
+         * Be careful with the mask, you don't want to mess with node code
+         */
+        InterlockedOr((volatile long *)&PrivateCacheMap->UlongFlags, PRIVATE_CACHE_MAP_READ_AHEAD_ACTIVE);
+        KeReleaseSpinLock(&PrivateCacheMap->ReadAheadSpinLock, OldIrql);
+
+        /* Get a work item */
+        WorkItem = ExAllocateFromNPagedLookasideList(&CcTwilightLookasideList);
+        if (WorkItem != NULL)
+        {
+            /* Reference our FO so that it doesn't go in between */
+            ObReferenceObject(FileObject);
+
+            /* We want to do read ahead! */
+            WorkItem->Function = ReadAhead;
+            WorkItem->Parameters.Read.FileObject = FileObject;
+
+            /* Queue in the read ahead dedicated queue */
+            CcPostWorkQueue(WorkItem, &CcExpressWorkQueue);
+
+            return;
+        }
+
+        /* Fail path: lock again, and revert read ahead active */
+        KeAcquireSpinLock(&PrivateCacheMap->ReadAheadSpinLock, &OldIrql);
+        InterlockedAnd((volatile long *)&PrivateCacheMap->UlongFlags, ~PRIVATE_CACHE_MAP_READ_AHEAD_ACTIVE);
+    }
+
+    /* Done (fail) */
+    KeReleaseSpinLock(&PrivateCacheMap->ReadAheadSpinLock, OldIrql);
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 VOID
 NTAPI
@@ -101,10 +267,35 @@ CcSetAdditionalCacheAttributes (
        IN      BOOLEAN         DisableWriteBehind
        )
 {
+    KIRQL OldIrql;
+    PROS_SHARED_CACHE_MAP SharedCacheMap;
+
     CCTRACE(CC_API_DEBUG, "FileObject=%p DisableReadAhead=%d DisableWriteBehind=%d\n",
         FileObject, DisableReadAhead, DisableWriteBehind);
 
-       UNIMPLEMENTED;
+    SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+
+    OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
+
+    if (DisableReadAhead)
+    {
+        SetFlag(SharedCacheMap->Flags, READAHEAD_DISABLED);
+    }
+    else
+    {
+        ClearFlag(SharedCacheMap->Flags, READAHEAD_DISABLED);
+    }
+
+    if (DisableWriteBehind)
+    {
+        /* FIXME: also set flag 0x200 */
+        SetFlag(SharedCacheMap->Flags, WRITEBEHIND_DISABLED);
+    }
+    else
+    {
+        ClearFlag(SharedCacheMap->Flags, WRITEBEHIND_DISABLED);
+    }
+    KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
 }
 
 /*
@@ -117,14 +308,22 @@ CcSetBcbOwnerPointer (
        IN      PVOID   Owner
        )
 {
+    PINTERNAL_BCB iBcb = Bcb;
+
     CCTRACE(CC_API_DEBUG, "Bcb=%p Owner=%p\n",
         Bcb, Owner);
 
-       UNIMPLEMENTED;
+    if (!ExIsResourceAcquiredExclusiveLite(&iBcb->Lock) && !ExIsResourceAcquiredSharedLite(&iBcb->Lock))
+    {
+        DPRINT1("Current thread doesn't own resource!\n");
+        return;
+    }
+
+    ExSetResourceOwnerPointer(&iBcb->Lock, Owner);
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 VOID
 NTAPI
@@ -133,14 +332,27 @@ CcSetDirtyPageThreshold (
        IN      ULONG           DirtyPageThreshold
        )
 {
+    PFSRTL_COMMON_FCB_HEADER Fcb;
+    PROS_SHARED_CACHE_MAP SharedCacheMap;
+
     CCTRACE(CC_API_DEBUG, "FileObject=%p DirtyPageThreshold=%lu\n",
         FileObject, DirtyPageThreshold);
 
-       UNIMPLEMENTED;
+    SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
+    if (SharedCacheMap != NULL)
+    {
+        SharedCacheMap->DirtyPageThreshold = DirtyPageThreshold;
+    }
+
+    Fcb = FileObject->FsContext;
+    if (!BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES))
+    {
+        SetFlag(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES);
+    }
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 VOID
 NTAPI
@@ -149,8 +361,11 @@ CcSetReadAheadGranularity (
        IN      ULONG           Granularity
        )
 {
+    PPRIVATE_CACHE_MAP PrivateMap;
+
     CCTRACE(CC_API_DEBUG, "FileObject=%p Granularity=%lu\n",
         FileObject, Granularity);
 
-       UNIMPLEMENTED;
+    PrivateMap = FileObject->PrivateCacheMap;
+    PrivateMap->ReadAheadMask = Granularity - 1;
 }