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
)
401 UNIMPLEMENTED_DBGBREAK();
406 This could be implemented much more intelligently by mapping instances
407 of a CoW zero page into the affected regions. We just RtlZeroMemory
413 CcZeroData(IN PFILE_OBJECT FileObject
,
414 IN PLARGE_INTEGER StartOffset
,
415 IN PLARGE_INTEGER EndOffset
,
418 PNOCC_BCB Bcb
= NULL
;
419 PLIST_ENTRY ListEntry
= NULL
;
420 LARGE_INTEGER LowerBound
= *StartOffset
;
421 LARGE_INTEGER UpperBound
= *EndOffset
;
422 LARGE_INTEGER Target
, End
;
423 PVOID PinnedBcb
, PinnedBuffer
;
424 PNOCC_CACHE_MAP Map
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
426 DPRINT("S %08x%08x E %08x%08x\n",
427 StartOffset
->u
.HighPart
,
428 StartOffset
->u
.LowPart
,
429 EndOffset
->u
.HighPart
,
430 EndOffset
->u
.LowPart
);
435 IO_STATUS_BLOCK IOSB
;
436 PCHAR ZeroBuf
= ExAllocatePool(PagedPool
, PAGE_SIZE
);
439 if (!ZeroBuf
) RtlRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
440 DPRINT1("RtlZeroMemory(%x,%x)\n", ZeroBuf
, PAGE_SIZE
);
441 RtlZeroMemory(ZeroBuf
, PAGE_SIZE
);
443 Target
.QuadPart
= PAGE_ROUND_DOWN(LowerBound
.QuadPart
);
444 End
.QuadPart
= PAGE_ROUND_UP(UpperBound
.QuadPart
);
446 // Handle leading page
447 if (LowerBound
.QuadPart
!= Target
.QuadPart
)
449 ToWrite
= MIN(UpperBound
.QuadPart
- LowerBound
.QuadPart
,
450 (PAGE_SIZE
- LowerBound
.QuadPart
) & (PAGE_SIZE
- 1));
452 DPRINT("Zero last half %08x%08x %x\n",
457 Status
= MiSimpleRead(FileObject
,
464 if (!NT_SUCCESS(Status
))
467 RtlRaiseStatus(Status
);
470 DPRINT1("RtlZeroMemory(%x,%x)\n",
471 ZeroBuf
+ LowerBound
.QuadPart
- Target
.QuadPart
,
474 RtlZeroMemory(ZeroBuf
+ LowerBound
.QuadPart
- Target
.QuadPart
,
477 Status
= MiSimpleWrite(FileObject
,
481 UpperBound
.QuadPart
-Target
.QuadPart
),
484 if (!NT_SUCCESS(Status
))
487 RtlRaiseStatus(Status
);
489 Target
.QuadPart
+= PAGE_SIZE
;
492 DPRINT1("RtlZeroMemory(%x,%x)\n", ZeroBuf
, PAGE_SIZE
);
493 RtlZeroMemory(ZeroBuf
, PAGE_SIZE
);
495 while (UpperBound
.QuadPart
- Target
.QuadPart
> PAGE_SIZE
)
497 DPRINT("Zero full page %08x%08x\n",
501 Status
= MiSimpleWrite(FileObject
,
507 if (!NT_SUCCESS(Status
))
510 RtlRaiseStatus(Status
);
512 Target
.QuadPart
+= PAGE_SIZE
;
515 if (UpperBound
.QuadPart
> Target
.QuadPart
)
517 ToWrite
= UpperBound
.QuadPart
- Target
.QuadPart
;
518 DPRINT("Zero first half %08x%08x %x\n",
523 Status
= MiSimpleRead(FileObject
,
530 if (!NT_SUCCESS(Status
))
533 RtlRaiseStatus(Status
);
535 DPRINT1("RtlZeroMemory(%x,%x)\n", ZeroBuf
, ToWrite
);
536 RtlZeroMemory(ZeroBuf
, ToWrite
);
537 Status
= MiSimpleWrite(FileObject
,
541 UpperBound
.QuadPart
-Target
.QuadPart
),
543 if (!NT_SUCCESS(Status
))
546 RtlRaiseStatus(Status
);
548 Target
.QuadPart
+= PAGE_SIZE
;
556 ListEntry
= Map
->AssociatedBcb
.Flink
;
558 while (ListEntry
!= &Map
->AssociatedBcb
)
560 Bcb
= CONTAINING_RECORD(ListEntry
, NOCC_BCB
, ThisFileList
);
561 CcpReferenceCache(Bcb
- CcCacheSections
);
563 if (Bcb
->FileOffset
.QuadPart
+ Bcb
->Length
>= LowerBound
.QuadPart
&&
564 Bcb
->FileOffset
.QuadPart
< UpperBound
.QuadPart
)
566 DPRINT("Bcb #%x (@%08x%08x)\n",
567 Bcb
- CcCacheSections
,
568 Bcb
->FileOffset
.u
.HighPart
,
569 Bcb
->FileOffset
.u
.LowPart
);
571 Target
.QuadPart
= MAX(Bcb
->FileOffset
.QuadPart
,
572 LowerBound
.QuadPart
);
574 End
.QuadPart
= MIN(Map
->FileSizes
.ValidDataLength
.QuadPart
,
575 UpperBound
.QuadPart
);
577 End
.QuadPart
= MIN(End
.QuadPart
,
578 Bcb
->FileOffset
.QuadPart
+ Bcb
->Length
);
582 if (!CcPreparePinWrite(FileObject
,
584 End
.QuadPart
- Target
.QuadPart
,
593 ASSERT(PinnedBcb
== Bcb
);
596 ListEntry
= ListEntry
->Flink
;
597 /* Return from pin state */
598 CcpUnpinData(PinnedBcb
, TRUE
);
601 CcpUnpinData(Bcb
, TRUE
);
611 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointer
)
613 PFILE_OBJECT Result
= NULL
;
614 PNOCC_CACHE_MAP Map
= SectionObjectPointer
->SharedCacheMap
;
616 if (!IsListEmpty(&Map
->AssociatedBcb
))
618 PNOCC_BCB Bcb
= CONTAINING_RECORD(Map
->AssociatedBcb
.Flink
,
622 Result
= MmGetFileObjectForSection((PROS_SECTION_OBJECT
)Bcb
->SectionObject
);
630 CcGetFileObjectFromBcb(PVOID Bcb
)
632 PNOCC_BCB RealBcb
= (PNOCC_BCB
)Bcb
;
633 DPRINT("BCB #%x\n", RealBcb
- CcCacheSections
);
634 return MmGetFileObjectForSection((PROS_SECTION_OBJECT
)RealBcb
->SectionObject
);