xref: /reactos/ntoskrnl/cc/cacheman.c (revision 12e94103)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/cc/cacheman.c
5  * PURPOSE:         Cache manager
6  *
7  * PROGRAMMERS:     David Welch (welch@cwcom.net)
8  *                  Pierre Schweitzer (pierre@reactos.org)
9  */
10 
11 /* INCLUDES *****************************************************************/
12 
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16 
17 BOOLEAN CcPfEnablePrefetcher;
18 PFSN_PREFETCHER_GLOBALS CcPfGlobals;
19 MM_SYSTEMSIZE CcCapturedSystemSize;
20 
21 static ULONG BugCheckFileId = 0x4 << 16;
22 
23 /* FUNCTIONS *****************************************************************/
24 
25 VOID
26 NTAPI
27 INIT_FUNCTION
28 CcPfInitializePrefetcher(VOID)
29 {
30     /* Notify debugger */
31     DbgPrintEx(DPFLTR_PREFETCHER_ID,
32                DPFLTR_TRACE_LEVEL,
33                "CCPF: InitializePrefetecher()\n");
34 
35     /* Setup the Prefetcher Data */
36     InitializeListHead(&CcPfGlobals.ActiveTraces);
37     InitializeListHead(&CcPfGlobals.CompletedTraces);
38     ExInitializeFastMutex(&CcPfGlobals.CompletedTracesLock);
39 
40     /* FIXME: Setup the rest of the prefetecher */
41 }
42 
43 BOOLEAN
44 NTAPI
45 INIT_FUNCTION
46 CcInitializeCacheManager(VOID)
47 {
48     ULONG Thread;
49 
50     CcInitView();
51 
52     /* Initialize lazy-writer lists */
53     InitializeListHead(&CcIdleWorkerThreadList);
54     InitializeListHead(&CcExpressWorkQueue);
55     InitializeListHead(&CcRegularWorkQueue);
56     InitializeListHead(&CcPostTickWorkQueue);
57 
58     /* Define lazy writer threshold and the amount of workers,
59       * depending on the system type
60       */
61     CcCapturedSystemSize = MmQuerySystemSize();
62     switch (CcCapturedSystemSize)
63     {
64         case MmSmallSystem:
65             CcNumberWorkerThreads = ExCriticalWorkerThreads - 1;
66             CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8;
67             break;
68 
69         case MmMediumSystem:
70             CcNumberWorkerThreads = ExCriticalWorkerThreads - 1;
71             CcDirtyPageThreshold = MmNumberOfPhysicalPages / 4;
72             break;
73 
74         case MmLargeSystem:
75             CcNumberWorkerThreads = ExCriticalWorkerThreads - 2;
76             CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8 + MmNumberOfPhysicalPages / 4;
77             break;
78 
79         default:
80             CcNumberWorkerThreads = 1;
81             CcDirtyPageThreshold = MmNumberOfPhysicalPages / 8;
82             break;
83     }
84 
85     /* Allocate a work item for all our threads */
86     for (Thread = 0; Thread < CcNumberWorkerThreads; ++Thread)
87     {
88         PWORK_QUEUE_ITEM Item;
89 
90         Item = ExAllocatePoolWithTag(NonPagedPool, sizeof(WORK_QUEUE_ITEM), 'qWcC');
91         if (Item == NULL)
92         {
93             CcBugCheck(0, 0, 0);
94         }
95 
96         /* By default, it's obviously idle */
97         ExInitializeWorkItem(Item, CcWorkerThread, Item);
98         InsertTailList(&CcIdleWorkerThreadList, &Item->List);
99     }
100 
101     /* Initialize our lazy writer */
102     RtlZeroMemory(&LazyWriter, sizeof(LazyWriter));
103     InitializeListHead(&LazyWriter.WorkQueue);
104     /* Delay activation of the lazy writer */
105     KeInitializeDpc(&LazyWriter.ScanDpc, CcScanDpc, NULL);
106     KeInitializeTimer(&LazyWriter.ScanTimer);
107 
108     /* Lookaside list for our work items */
109     ExInitializeNPagedLookasideList(&CcTwilightLookasideList, NULL, NULL, 0, sizeof(WORK_QUEUE_ENTRY), 'KWcC', 0);
110 
111     return TRUE;
112 }
113 
114 VOID
115 NTAPI
116 CcShutdownSystem(VOID)
117 {
118     /* NOTHING TO DO */
119 }
120 
121 /*
122  * @unimplemented
123  */
124 LARGE_INTEGER
125 NTAPI
126 CcGetFlushedValidData (
127     IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
128     IN BOOLEAN BcbListHeld
129     )
130 {
131 	LARGE_INTEGER i;
132 
133 	UNIMPLEMENTED;
134 
135 	i.QuadPart = 0;
136 	return i;
137 }
138 
139 /*
140  * @unimplemented
141  */
142 PVOID
143 NTAPI
144 CcRemapBcb (
145     IN PVOID Bcb
146     )
147 {
148 	UNIMPLEMENTED;
149 
150     return 0;
151 }
152 
153 /*
154  * @unimplemented
155  */
156 VOID
157 NTAPI
158 CcScheduleReadAhead (
159 	IN	PFILE_OBJECT		FileObject,
160 	IN	PLARGE_INTEGER		FileOffset,
161 	IN	ULONG			Length
162 	)
163 {
164     KIRQL OldIrql;
165     LARGE_INTEGER NewOffset;
166     PROS_SHARED_CACHE_MAP SharedCacheMap;
167     PPRIVATE_CACHE_MAP PrivateCacheMap;
168     static ULONG Warn;
169 
170     SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
171     PrivateCacheMap = FileObject->PrivateCacheMap;
172 
173     /* If file isn't cached, or if read ahead is disabled, this is no op */
174     if (SharedCacheMap == NULL || PrivateCacheMap == NULL ||
175         BooleanFlagOn(SharedCacheMap->Flags, READAHEAD_DISABLED))
176     {
177         return;
178     }
179 
180     /* Round read length with read ahead mask */
181     Length = ROUND_UP(Length, PrivateCacheMap->ReadAheadMask + 1);
182     /* Compute the offset we'll reach */
183     NewOffset.QuadPart = FileOffset->QuadPart + Length;
184 
185     /* Lock read ahead spin lock */
186     KeAcquireSpinLock(&PrivateCacheMap->ReadAheadSpinLock, &OldIrql);
187     /* Easy case: the file is sequentially read */
188     if (BooleanFlagOn(FileObject->Flags, FO_SEQUENTIAL_ONLY))
189     {
190         /* If we went backward, this is no go! */
191         if (NewOffset.QuadPart < PrivateCacheMap->ReadAheadOffset[1].QuadPart)
192         {
193             KeReleaseSpinLock(&PrivateCacheMap->ReadAheadSpinLock, OldIrql);
194             return;
195         }
196 
197         /* FIXME: hackish, but will do the job for now */
198         PrivateCacheMap->ReadAheadOffset[1].QuadPart = NewOffset.QuadPart;
199         PrivateCacheMap->ReadAheadLength[1] = Length;
200     }
201     /* Other cases: try to find some logic in that mess... */
202     else
203     {
204         /* Let's check if we always read the same way (like going down in the file)
205          * and pretend it's enough for now
206          */
207         if (PrivateCacheMap->FileOffset2.QuadPart >= PrivateCacheMap->FileOffset1.QuadPart &&
208             FileOffset->QuadPart >= PrivateCacheMap->FileOffset2.QuadPart)
209         {
210             /* FIXME: hackish, but will do the job for now */
211             PrivateCacheMap->ReadAheadOffset[1].QuadPart = NewOffset.QuadPart;
212             PrivateCacheMap->ReadAheadLength[1] = Length;
213         }
214         else
215         {
216             /* FIXME: handle the other cases */
217             KeReleaseSpinLock(&PrivateCacheMap->ReadAheadSpinLock, OldIrql);
218 	        if (!Warn++) UNIMPLEMENTED;
219             return;
220         }
221     }
222 
223     /* If read ahead isn't active yet */
224     if (!PrivateCacheMap->Flags.ReadAheadActive)
225     {
226         PWORK_QUEUE_ENTRY WorkItem;
227 
228         /* It's active now!
229          * Be careful with the mask, you don't want to mess with node code
230          */
231         InterlockedOr((volatile long *)&PrivateCacheMap->UlongFlags, PRIVATE_CACHE_MAP_READ_AHEAD_ACTIVE);
232         KeReleaseSpinLock(&PrivateCacheMap->ReadAheadSpinLock, OldIrql);
233 
234         /* Get a work item */
235         WorkItem = ExAllocateFromNPagedLookasideList(&CcTwilightLookasideList);
236         if (WorkItem != NULL)
237         {
238             /* Reference our FO so that it doesn't go in between */
239             ObReferenceObject(FileObject);
240 
241             /* We want to do read ahead! */
242             WorkItem->Function = ReadAhead;
243             WorkItem->Parameters.Read.FileObject = FileObject;
244 
245             /* Queue in the read ahead dedicated queue */
246             CcPostWorkQueue(WorkItem, &CcExpressWorkQueue);
247 
248             return;
249         }
250 
251         /* Fail path: lock again, and revert read ahead active */
252         KeAcquireSpinLock(&PrivateCacheMap->ReadAheadSpinLock, &OldIrql);
253         InterlockedAnd((volatile long *)&PrivateCacheMap->UlongFlags, ~PRIVATE_CACHE_MAP_READ_AHEAD_ACTIVE);
254     }
255 
256     /* Done (fail) */
257     KeReleaseSpinLock(&PrivateCacheMap->ReadAheadSpinLock, OldIrql);
258 }
259 
260 /*
261  * @implemented
262  */
263 VOID
264 NTAPI
265 CcSetAdditionalCacheAttributes (
266 	IN	PFILE_OBJECT	FileObject,
267 	IN	BOOLEAN		DisableReadAhead,
268 	IN	BOOLEAN		DisableWriteBehind
269 	)
270 {
271     KIRQL OldIrql;
272     PROS_SHARED_CACHE_MAP SharedCacheMap;
273 
274     CCTRACE(CC_API_DEBUG, "FileObject=%p DisableReadAhead=%d DisableWriteBehind=%d\n",
275         FileObject, DisableReadAhead, DisableWriteBehind);
276 
277     SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
278 
279     OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
280 
281     if (DisableReadAhead)
282     {
283         SetFlag(SharedCacheMap->Flags, READAHEAD_DISABLED);
284     }
285     else
286     {
287         ClearFlag(SharedCacheMap->Flags, READAHEAD_DISABLED);
288     }
289 
290     if (DisableWriteBehind)
291     {
292         /* FIXME: also set flag 0x200 */
293         SetFlag(SharedCacheMap->Flags, WRITEBEHIND_DISABLED);
294     }
295     else
296     {
297         ClearFlag(SharedCacheMap->Flags, WRITEBEHIND_DISABLED);
298     }
299     KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
300 }
301 
302 /*
303  * @unimplemented
304  */
305 VOID
306 NTAPI
307 CcSetBcbOwnerPointer (
308 	IN	PVOID	Bcb,
309 	IN	PVOID	Owner
310 	)
311 {
312     PINTERNAL_BCB iBcb = Bcb;
313 
314     CCTRACE(CC_API_DEBUG, "Bcb=%p Owner=%p\n",
315         Bcb, Owner);
316 
317     if (!ExIsResourceAcquiredExclusiveLite(&iBcb->Lock) && !ExIsResourceAcquiredSharedLite(&iBcb->Lock))
318     {
319         DPRINT1("Current thread doesn't own resource!\n");
320         return;
321     }
322 
323     ExSetResourceOwnerPointer(&iBcb->Lock, Owner);
324 }
325 
326 /*
327  * @implemented
328  */
329 VOID
330 NTAPI
331 CcSetDirtyPageThreshold (
332 	IN	PFILE_OBJECT	FileObject,
333 	IN	ULONG		DirtyPageThreshold
334 	)
335 {
336     PFSRTL_COMMON_FCB_HEADER Fcb;
337     PROS_SHARED_CACHE_MAP SharedCacheMap;
338 
339     CCTRACE(CC_API_DEBUG, "FileObject=%p DirtyPageThreshold=%lu\n",
340         FileObject, DirtyPageThreshold);
341 
342     SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
343     if (SharedCacheMap != NULL)
344     {
345         SharedCacheMap->DirtyPageThreshold = DirtyPageThreshold;
346     }
347 
348     Fcb = FileObject->FsContext;
349     if (!BooleanFlagOn(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES))
350     {
351         SetFlag(Fcb->Flags, FSRTL_FLAG_LIMIT_MODIFIED_PAGES);
352     }
353 }
354 
355 /*
356  * @implemented
357  */
358 VOID
359 NTAPI
360 CcSetReadAheadGranularity (
361 	IN	PFILE_OBJECT	FileObject,
362 	IN	ULONG		Granularity
363 	)
364 {
365     PPRIVATE_CACHE_MAP PrivateMap;
366 
367     CCTRACE(CC_API_DEBUG, "FileObject=%p Granularity=%lu\n",
368         FileObject, Granularity);
369 
370     PrivateMap = FileObject->PrivateCacheMap;
371     PrivateMap->ReadAheadMask = Granularity - 1;
372 }
373