2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/cache/fssup.c
5 * PURPOSE: Logging and configuration routines
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
10 /* INCLUDES *******************************************************************/
14 #include "section/newmm.h"
18 /* GLOBALS ********************************************************************/
20 PFSN_PREFETCHER_GLOBALS CcPfGlobals
;
21 extern LONG CcOutstandingDeletes
;
22 extern KEVENT CcpLazyWriteEvent
;
23 extern KEVENT CcFinalizeEvent
;
24 extern VOID NTAPI
CcpUnmapThread(PVOID Unused
);
25 extern VOID NTAPI
CcpLazyWriteThread(PVOID Unused
);
26 HANDLE CcUnmapThreadHandle
, CcLazyWriteThreadHandle
;
27 CLIENT_ID CcUnmapThreadId
, CcLazyWriteThreadId
;
28 FAST_MUTEX GlobalPageOperation
;
32 A note about private cache maps.
34 CcInitializeCacheMap and CcUninitializeCacheMap are not meant to be paired,
35 although they can work that way.
37 The actual operation I've gleaned from reading both jan kratchovil's writing
38 and real filesystems is this:
40 CcInitializeCacheMap means:
42 Make the indicated FILE_OBJECT have a private cache map if it doesn't already
43 and make it have a shared cache map if it doesn't already.
45 CcUninitializeCacheMap means:
47 Take away the private cache map from this FILE_OBJECT. If it's the last
48 private cache map corresponding to a specific shared cache map (the one that
49 was present in the FILE_OBJECT when it was created), then delete that too,
50 flusing all cached information.
52 Using these simple semantics, filesystems can do all the things they actually
55 - Copy out the shared cache map pointer from a newly initialized file object
56 and store it in the fcb cache.
57 - Copy it back into any file object and call CcInitializeCacheMap to make
58 that file object be associated with the caching of all the other siblings.
59 - Call CcUninitializeCacheMap on a FILE_OBJECT many times, but have only the
60 first one count for each specific FILE_OBJECT.
61 - Have the actual last call to CcUninitializeCacheMap (that is, the one that
62 causes zero private cache maps to be associated with a shared cache map) to
63 delete the cache map and flush.
65 So private cache map here is a light weight structure that just remembers
66 what shared cache map it associates with.
69 typedef struct _NOCC_PRIVATE_CACHE_MAP
72 PFILE_OBJECT FileObject
;
74 } NOCC_PRIVATE_CACHE_MAP
, *PNOCC_PRIVATE_CACHE_MAP
;
76 LIST_ENTRY CcpAllSharedCacheMaps
;
78 /* FUNCTIONS ******************************************************************/
82 CcInitializeCacheManager(VOID
)
86 DPRINT("Initialize\n");
87 for (i
= 0; i
< CACHE_NUM_SECTIONS
; i
++)
89 KeInitializeEvent(&CcCacheSections
[i
].ExclusiveWait
,
93 InitializeListHead(&CcCacheSections
[i
].ThisFileList
);
96 InitializeListHead(&CcpAllSharedCacheMaps
);
98 KeInitializeEvent(&CcDeleteEvent
, SynchronizationEvent
, FALSE
);
99 KeInitializeEvent(&CcFinalizeEvent
, SynchronizationEvent
, FALSE
);
100 KeInitializeEvent(&CcpLazyWriteEvent
, SynchronizationEvent
, FALSE
);
102 CcCacheBitmap
->Buffer
= ((PULONG
)&CcCacheBitmap
[1]);
103 CcCacheBitmap
->SizeOfBitMap
= ROUND_UP(CACHE_NUM_SECTIONS
, 32);
104 DPRINT1("Cache has %d entries\n", CcCacheBitmap
->SizeOfBitMap
);
105 ExInitializeFastMutex(&CcMutex
);
112 CcPfInitializePrefetcher(VOID
)
114 /* Notify debugger */
115 DbgPrintEx(DPFLTR_PREFETCHER_ID
,
117 "CCPF: InitializePrefetecher()\n");
119 /* Setup the Prefetcher Data */
120 InitializeListHead(&CcPfGlobals
.ActiveTraces
);
121 InitializeListHead(&CcPfGlobals
.CompletedTraces
);
122 ExInitializeFastMutex(&CcPfGlobals
.CompletedTracesLock
);
124 /* FIXME: Setup the rest of the prefetecher */
129 CcpAcquireFileLock(PNOCC_CACHE_MAP Map
)
131 DPRINT("Calling AcquireForLazyWrite: %x\n", Map
->LazyContext
);
132 return Map
->Callbacks
.AcquireForLazyWrite(Map
->LazyContext
, TRUE
);
137 CcpReleaseFileLock(PNOCC_CACHE_MAP Map
)
139 DPRINT("Releasing Lazy Write %x\n", Map
->LazyContext
);
140 Map
->Callbacks
.ReleaseFromLazyWrite(Map
->LazyContext
);
145 Cc functions are required to treat alternate streams of a file as the same
146 for the purpose of caching, meaning that we must be able to find the shared
147 cache map associated with the ``real'' stream associated with a stream file
148 object, if one exists. We do that by identifying a private cache map in
149 our gamut that has the same volume, device and fscontext as the stream file
150 object we're holding. It's heavy but it does work. This can probably be
151 improved, although there doesn't seem to be any real association between
152 a stream file object and a sibling file object in the file object struct
157 /* Must have CcpLock() */
158 PFILE_OBJECT
CcpFindOtherStreamFileObject(PFILE_OBJECT FileObject
)
160 PLIST_ENTRY Entry
, Private
;
161 for (Entry
= CcpAllSharedCacheMaps
.Flink
;
162 Entry
!= &CcpAllSharedCacheMaps
;
163 Entry
= Entry
->Flink
)
165 /* 'Identical' test for other stream file object */
166 PNOCC_CACHE_MAP Map
= CONTAINING_RECORD(Entry
, NOCC_CACHE_MAP
, Entry
);
167 for (Private
= Map
->PrivateCacheMaps
.Flink
;
168 Private
!= &Map
->PrivateCacheMaps
;
169 Private
= Private
->Flink
)
171 PNOCC_PRIVATE_CACHE_MAP PrivateMap
= CONTAINING_RECORD(Private
,
172 NOCC_PRIVATE_CACHE_MAP
,
175 if (PrivateMap
->FileObject
->Flags
& FO_STREAM_FILE
&&
176 PrivateMap
->FileObject
->DeviceObject
== FileObject
->DeviceObject
&&
177 PrivateMap
->FileObject
->Vpb
== FileObject
->Vpb
&&
178 PrivateMap
->FileObject
->FsContext
== FileObject
->FsContext
&&
179 PrivateMap
->FileObject
->FsContext2
== FileObject
->FsContext2
&&
182 return PrivateMap
->FileObject
;
189 /* Thanks: http://windowsitpro.com/Windows/Articles/ArticleID/3864/pg/2/2.html */
193 CcInitializeCacheMap(IN PFILE_OBJECT FileObject
,
194 IN PCC_FILE_SIZES FileSizes
,
195 IN BOOLEAN PinAccess
,
196 IN PCACHE_MANAGER_CALLBACKS Callbacks
,
197 IN PVOID LazyWriteContext
)
199 PNOCC_CACHE_MAP Map
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
200 PNOCC_PRIVATE_CACHE_MAP PrivateCacheMap
= FileObject
->PrivateCacheMap
;
203 /* We don't have a shared cache map. First find out if we have a sibling
204 stream file object we can take it from. */
205 if (!Map
&& FileObject
->Flags
& FO_STREAM_FILE
)
207 PFILE_OBJECT IdenticalStreamFileObject
= CcpFindOtherStreamFileObject(FileObject
);
208 if (IdenticalStreamFileObject
)
209 Map
= IdenticalStreamFileObject
->SectionObjectPointer
->SharedCacheMap
;
212 DPRINT1("Linking SFO %x to previous SFO %x through cache map %x #\n",
214 IdenticalStreamFileObject
,
218 /* We still don't have a shared cache map. We need to create one. */
221 DPRINT("Initializing file object for (%p) %wZ\n",
223 &FileObject
->FileName
);
225 Map
= ExAllocatePool(NonPagedPool
, sizeof(NOCC_CACHE_MAP
));
226 FileObject
->SectionObjectPointer
->SharedCacheMap
= Map
;
227 Map
->FileSizes
= *FileSizes
;
228 Map
->LazyContext
= LazyWriteContext
;
229 Map
->ReadAheadGranularity
= PAGE_SIZE
;
230 RtlCopyMemory(&Map
->Callbacks
, Callbacks
, sizeof(*Callbacks
));
233 DPRINT("FileSizes->ValidDataLength %08x%08x\n",
234 FileSizes
->ValidDataLength
.HighPart
,
235 FileSizes
->ValidDataLength
.LowPart
);
237 InitializeListHead(&Map
->AssociatedBcb
);
238 InitializeListHead(&Map
->PrivateCacheMaps
);
239 InsertTailList(&CcpAllSharedCacheMaps
, &Map
->Entry
);
240 DPRINT("New Map %x\n", Map
);
242 /* We don't have a private cache map. Link it with the shared cache map
243 to serve as a held reference. When the list in the shared cache map
244 is empty, we know we can delete it. */
245 if (!PrivateCacheMap
)
247 PrivateCacheMap
= ExAllocatePool(NonPagedPool
,
248 sizeof(*PrivateCacheMap
));
250 FileObject
->PrivateCacheMap
= PrivateCacheMap
;
251 PrivateCacheMap
->FileObject
= FileObject
;
252 ObReferenceObject(PrivateCacheMap
->FileObject
);
255 PrivateCacheMap
->Map
= Map
;
256 InsertTailList(&Map
->PrivateCacheMaps
, &PrivateCacheMap
->ListEntry
);
263 This function is used by NewCC's MM to determine whether any section objects
264 for a given file are not cache sections. If that's true, we're not allowed
265 to resize the file, although nothing actually prevents us from doing ;-)
271 CcpCountCacheSections(IN PNOCC_CACHE_MAP Map
)
276 for (Count
= 0, Entry
= Map
->AssociatedBcb
.Flink
;
277 Entry
!= &Map
->AssociatedBcb
;
278 Entry
= Entry
->Flink
, Count
++);
285 CcUninitializeCacheMap(IN PFILE_OBJECT FileObject
,
286 IN OPTIONAL PLARGE_INTEGER TruncateSize
,
287 IN OPTIONAL PCACHE_UNINITIALIZE_EVENT UninitializeEvent
)
289 BOOLEAN LastMap
= FALSE
;
290 PNOCC_CACHE_MAP Map
= (PNOCC_CACHE_MAP
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
291 PNOCC_PRIVATE_CACHE_MAP PrivateCacheMap
= FileObject
->PrivateCacheMap
;
293 DPRINT("Uninitializing file object for %wZ SectionObjectPointer %x\n",
294 &FileObject
->FileName
,
295 FileObject
->SectionObjectPointer
);
297 ASSERT(UninitializeEvent
== NULL
);
299 /* It may not be strictly necessary to flush here, but we do just for
302 CcpFlushCache(Map
, NULL
, 0, NULL
, FALSE
);
305 /* We have a private cache map, so we've been initialized and haven't been
309 ASSERT(!Map
|| Map
== PrivateCacheMap
->Map
);
310 ASSERT(PrivateCacheMap
->FileObject
== FileObject
);
312 RemoveEntryList(&PrivateCacheMap
->ListEntry
);
313 /* That was the last private cache map. It's time to delete all
314 cache stripes and all aspects of caching on the file. */
315 if (IsListEmpty(&PrivateCacheMap
->Map
->PrivateCacheMaps
))
317 /* Get rid of all the cache stripes. */
318 while (!IsListEmpty(&PrivateCacheMap
->Map
->AssociatedBcb
))
320 PNOCC_BCB Bcb
= CONTAINING_RECORD(PrivateCacheMap
->Map
->AssociatedBcb
.Flink
,
324 DPRINT("Evicting cache stripe #%x\n", Bcb
- CcCacheSections
);
326 CcpDereferenceCache(Bcb
- CcCacheSections
, TRUE
);
328 RemoveEntryList(&PrivateCacheMap
->Map
->Entry
);
329 ExFreePool(PrivateCacheMap
->Map
);
330 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
333 ObDereferenceObject(PrivateCacheMap
->FileObject
);
334 FileObject
->PrivateCacheMap
= NULL
;
335 ExFreePool(PrivateCacheMap
);
339 DPRINT("Uninit complete\n");
341 /* The return from CcUninitializeCacheMap means that 'caching was stopped'. */
347 CcSetFileSizes is used to tell the cache manager that the file changed
348 size. In our case, we use the internal Mm method MmExtendCacheSection
349 to notify Mm that our section potentially changed size, which may mean
355 CcSetFileSizes(IN PFILE_OBJECT FileObject
,
356 IN PCC_FILE_SIZES FileSizes
)
358 PNOCC_CACHE_MAP Map
= (PNOCC_CACHE_MAP
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
362 Map
->FileSizes
= *FileSizes
;
363 Bcb
= Map
->AssociatedBcb
.Flink
== &Map
->AssociatedBcb
?
364 NULL
: CONTAINING_RECORD(Map
->AssociatedBcb
.Flink
, NOCC_BCB
, ThisFileList
);
366 MmExtendCacheSection(Bcb
->SectionObject
, &FileSizes
->FileSize
, FALSE
);
367 DPRINT("FileSizes->FileSize %x\n", FileSizes
->FileSize
.LowPart
);
368 DPRINT("FileSizes->AllocationSize %x\n", FileSizes
->AllocationSize
.LowPart
);
369 DPRINT("FileSizes->ValidDataLength %x\n", FileSizes
->ValidDataLength
.LowPart
);
374 CcGetFileSizes(IN PFILE_OBJECT FileObject
,
375 IN PCC_FILE_SIZES FileSizes
)
377 PNOCC_CACHE_MAP Map
= (PNOCC_CACHE_MAP
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
378 if (!Map
) return FALSE
;
379 *FileSizes
= Map
->FileSizes
;
385 CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer
,
386 IN OPTIONAL PLARGE_INTEGER FileOffset
,
388 IN BOOLEAN UninitializeCacheMaps
)
390 PNOCC_CACHE_MAP Map
= (PNOCC_CACHE_MAP
)SectionObjectPointer
->SharedCacheMap
;
391 if (!Map
) return TRUE
;
392 CcpFlushCache(Map
, NULL
, 0, NULL
, TRUE
);
398 CcSetDirtyPageThreshold(IN PFILE_OBJECT FileObject
,
399 IN ULONG DirtyPageThreshold
)
407 This could be implemented much more intelligently by mapping instances
408 of a CoW zero page into the affected regions. We just RtlZeroMemory
414 CcZeroData(IN PFILE_OBJECT FileObject
,
415 IN PLARGE_INTEGER StartOffset
,
416 IN PLARGE_INTEGER EndOffset
,
419 PNOCC_BCB Bcb
= NULL
;
420 PLIST_ENTRY ListEntry
= NULL
;
421 LARGE_INTEGER LowerBound
= *StartOffset
;
422 LARGE_INTEGER UpperBound
= *EndOffset
;
423 LARGE_INTEGER Target
, End
;
424 PVOID PinnedBcb
, PinnedBuffer
;
425 PNOCC_CACHE_MAP Map
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
427 DPRINT("S %08x%08x E %08x%08x\n",
428 StartOffset
->u
.HighPart
,
429 StartOffset
->u
.LowPart
,
430 EndOffset
->u
.HighPart
,
431 EndOffset
->u
.LowPart
);
436 IO_STATUS_BLOCK IOSB
;
437 PCHAR ZeroBuf
= ExAllocatePool(PagedPool
, PAGE_SIZE
);
440 if (!ZeroBuf
) RtlRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
441 DPRINT1("RtlZeroMemory(%x,%x)\n", ZeroBuf
, PAGE_SIZE
);
442 RtlZeroMemory(ZeroBuf
, PAGE_SIZE
);
444 Target
.QuadPart
= PAGE_ROUND_DOWN(LowerBound
.QuadPart
);
445 End
.QuadPart
= PAGE_ROUND_UP(UpperBound
.QuadPart
);
447 // Handle leading page
448 if (LowerBound
.QuadPart
!= Target
.QuadPart
)
450 ToWrite
= MIN(UpperBound
.QuadPart
- LowerBound
.QuadPart
,
451 (PAGE_SIZE
- LowerBound
.QuadPart
) & (PAGE_SIZE
- 1));
453 DPRINT("Zero last half %08x%08x %x\n",
458 Status
= MiSimpleRead(FileObject
,
465 if (!NT_SUCCESS(Status
))
468 RtlRaiseStatus(Status
);
471 DPRINT1("RtlZeroMemory(%x,%x)\n",
472 ZeroBuf
+ LowerBound
.QuadPart
- Target
.QuadPart
,
475 RtlZeroMemory(ZeroBuf
+ LowerBound
.QuadPart
- Target
.QuadPart
,
478 Status
= MiSimpleWrite(FileObject
,
482 UpperBound
.QuadPart
-Target
.QuadPart
),
485 if (!NT_SUCCESS(Status
))
488 RtlRaiseStatus(Status
);
490 Target
.QuadPart
+= PAGE_SIZE
;
493 DPRINT1("RtlZeroMemory(%x,%x)\n", ZeroBuf
, PAGE_SIZE
);
494 RtlZeroMemory(ZeroBuf
, PAGE_SIZE
);
496 while (UpperBound
.QuadPart
- Target
.QuadPart
> PAGE_SIZE
)
498 DPRINT("Zero full page %08x%08x\n",
502 Status
= MiSimpleWrite(FileObject
,
508 if (!NT_SUCCESS(Status
))
511 RtlRaiseStatus(Status
);
513 Target
.QuadPart
+= PAGE_SIZE
;
516 if (UpperBound
.QuadPart
> Target
.QuadPart
)
518 ToWrite
= UpperBound
.QuadPart
- Target
.QuadPart
;
519 DPRINT("Zero first half %08x%08x %x\n",
524 Status
= MiSimpleRead(FileObject
,
531 if (!NT_SUCCESS(Status
))
534 RtlRaiseStatus(Status
);
536 DPRINT1("RtlZeroMemory(%x,%x)\n", ZeroBuf
, ToWrite
);
537 RtlZeroMemory(ZeroBuf
, ToWrite
);
538 Status
= MiSimpleWrite(FileObject
,
542 UpperBound
.QuadPart
-Target
.QuadPart
),
544 if (!NT_SUCCESS(Status
))
547 RtlRaiseStatus(Status
);
549 Target
.QuadPart
+= PAGE_SIZE
;
557 ListEntry
= Map
->AssociatedBcb
.Flink
;
559 while (ListEntry
!= &Map
->AssociatedBcb
)
561 Bcb
= CONTAINING_RECORD(ListEntry
, NOCC_BCB
, ThisFileList
);
562 CcpReferenceCache(Bcb
- CcCacheSections
);
564 if (Bcb
->FileOffset
.QuadPart
+ Bcb
->Length
>= LowerBound
.QuadPart
&&
565 Bcb
->FileOffset
.QuadPart
< UpperBound
.QuadPart
)
567 DPRINT("Bcb #%x (@%08x%08x)\n",
568 Bcb
- CcCacheSections
,
569 Bcb
->FileOffset
.u
.HighPart
,
570 Bcb
->FileOffset
.u
.LowPart
);
572 Target
.QuadPart
= MAX(Bcb
->FileOffset
.QuadPart
,
573 LowerBound
.QuadPart
);
575 End
.QuadPart
= MIN(Map
->FileSizes
.ValidDataLength
.QuadPart
,
576 UpperBound
.QuadPart
);
578 End
.QuadPart
= MIN(End
.QuadPart
,
579 Bcb
->FileOffset
.QuadPart
+ Bcb
->Length
);
583 if (!CcPreparePinWrite(FileObject
,
585 End
.QuadPart
- Target
.QuadPart
,
594 ASSERT(PinnedBcb
== Bcb
);
597 ListEntry
= ListEntry
->Flink
;
598 /* Return from pin state */
599 CcpUnpinData(PinnedBcb
, TRUE
);
602 CcpUnpinData(Bcb
, TRUE
);
612 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointer
)
614 PFILE_OBJECT Result
= NULL
;
615 PNOCC_CACHE_MAP Map
= SectionObjectPointer
->SharedCacheMap
;
617 if (!IsListEmpty(&Map
->AssociatedBcb
))
619 PNOCC_BCB Bcb
= CONTAINING_RECORD(Map
->AssociatedBcb
.Flink
,
623 Result
= MmGetFileObjectForSection((PROS_SECTION_OBJECT
)Bcb
->SectionObject
);
631 CcGetFileObjectFromBcb(PVOID Bcb
)
633 PNOCC_BCB RealBcb
= (PNOCC_BCB
)Bcb
;
634 DPRINT("BCB #%x\n", RealBcb
- CcCacheSections
);
635 return MmGetFileObjectForSection((PROS_SECTION_OBJECT
)RealBcb
->SectionObject
);