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 %I64x\n",
234 FileSizes
->ValidDataLength
.QuadPart
);
236 InitializeListHead(&Map
->AssociatedBcb
);
237 InitializeListHead(&Map
->PrivateCacheMaps
);
238 InsertTailList(&CcpAllSharedCacheMaps
, &Map
->Entry
);
239 DPRINT("New Map %p\n", Map
);
241 /* We don't have a private cache map. Link it with the shared cache map
242 to serve as a held reference. When the list in the shared cache map
243 is empty, we know we can delete it. */
244 if (!PrivateCacheMap
)
246 PrivateCacheMap
= ExAllocatePool(NonPagedPool
,
247 sizeof(*PrivateCacheMap
));
249 FileObject
->PrivateCacheMap
= PrivateCacheMap
;
250 PrivateCacheMap
->FileObject
= FileObject
;
251 ObReferenceObject(PrivateCacheMap
->FileObject
);
254 PrivateCacheMap
->Map
= Map
;
255 InsertTailList(&Map
->PrivateCacheMaps
, &PrivateCacheMap
->ListEntry
);
262 This function is used by NewCC's MM to determine whether any section objects
263 for a given file are not cache sections. If that's true, we're not allowed
264 to resize the file, although nothing actually prevents us from doing ;-)
270 CcpCountCacheSections(IN PNOCC_CACHE_MAP Map
)
275 for (Count
= 0, Entry
= Map
->AssociatedBcb
.Flink
;
276 Entry
!= &Map
->AssociatedBcb
;
277 Entry
= Entry
->Flink
, Count
++);
284 CcUninitializeCacheMap(IN PFILE_OBJECT FileObject
,
285 IN OPTIONAL PLARGE_INTEGER TruncateSize
,
286 IN OPTIONAL PCACHE_UNINITIALIZE_EVENT UninitializeEvent
)
288 BOOLEAN LastMap
= FALSE
;
289 PNOCC_CACHE_MAP Map
= (PNOCC_CACHE_MAP
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
290 PNOCC_PRIVATE_CACHE_MAP PrivateCacheMap
= FileObject
->PrivateCacheMap
;
292 DPRINT("Uninitializing file object for %wZ SectionObjectPointer %x\n",
293 &FileObject
->FileName
,
294 FileObject
->SectionObjectPointer
);
296 ASSERT(UninitializeEvent
== NULL
);
298 /* It may not be strictly necessary to flush here, but we do just for
301 CcpFlushCache(Map
, NULL
, 0, NULL
, FALSE
);
304 /* We have a private cache map, so we've been initialized and haven't been
308 ASSERT(!Map
|| Map
== PrivateCacheMap
->Map
);
309 ASSERT(PrivateCacheMap
->FileObject
== FileObject
);
311 RemoveEntryList(&PrivateCacheMap
->ListEntry
);
312 /* That was the last private cache map. It's time to delete all
313 cache stripes and all aspects of caching on the file. */
314 if (IsListEmpty(&PrivateCacheMap
->Map
->PrivateCacheMaps
))
316 /* Get rid of all the cache stripes. */
317 while (!IsListEmpty(&PrivateCacheMap
->Map
->AssociatedBcb
))
319 PNOCC_BCB Bcb
= CONTAINING_RECORD(PrivateCacheMap
->Map
->AssociatedBcb
.Flink
,
323 DPRINT("Evicting cache stripe #%x\n", Bcb
- CcCacheSections
);
325 CcpDereferenceCache(Bcb
- CcCacheSections
, TRUE
);
327 RemoveEntryList(&PrivateCacheMap
->Map
->Entry
);
328 ExFreePool(PrivateCacheMap
->Map
);
329 FileObject
->SectionObjectPointer
->SharedCacheMap
= NULL
;
332 ObDereferenceObject(PrivateCacheMap
->FileObject
);
333 FileObject
->PrivateCacheMap
= NULL
;
334 ExFreePool(PrivateCacheMap
);
338 DPRINT("Uninit complete\n");
340 /* The return from CcUninitializeCacheMap means that 'caching was stopped'. */
346 CcSetFileSizes is used to tell the cache manager that the file changed
347 size. In our case, we use the internal Mm method MmExtendCacheSection
348 to notify Mm that our section potentially changed size, which may mean
354 CcSetFileSizes(IN PFILE_OBJECT FileObject
,
355 IN PCC_FILE_SIZES FileSizes
)
357 PNOCC_CACHE_MAP Map
= (PNOCC_CACHE_MAP
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
361 Map
->FileSizes
= *FileSizes
;
362 Bcb
= Map
->AssociatedBcb
.Flink
== &Map
->AssociatedBcb
?
363 NULL
: CONTAINING_RECORD(Map
->AssociatedBcb
.Flink
, NOCC_BCB
, ThisFileList
);
365 MmExtendCacheSection(Bcb
->SectionObject
, &FileSizes
->FileSize
, FALSE
);
366 DPRINT("FileSizes->FileSize %x\n", FileSizes
->FileSize
.LowPart
);
367 DPRINT("FileSizes->AllocationSize %x\n", FileSizes
->AllocationSize
.LowPart
);
368 DPRINT("FileSizes->ValidDataLength %x\n", FileSizes
->ValidDataLength
.LowPart
);
373 CcGetFileSizes(IN PFILE_OBJECT FileObject
,
374 IN PCC_FILE_SIZES FileSizes
)
376 PNOCC_CACHE_MAP Map
= (PNOCC_CACHE_MAP
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
377 if (!Map
) return FALSE
;
378 *FileSizes
= Map
->FileSizes
;
384 CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer
,
385 IN OPTIONAL PLARGE_INTEGER FileOffset
,
387 IN BOOLEAN UninitializeCacheMaps
)
389 PNOCC_CACHE_MAP Map
= (PNOCC_CACHE_MAP
)SectionObjectPointer
->SharedCacheMap
;
390 if (!Map
) return TRUE
;
391 CcpFlushCache(Map
, NULL
, 0, NULL
, TRUE
);
397 CcSetDirtyPageThreshold(IN PFILE_OBJECT FileObject
,
398 IN ULONG DirtyPageThreshold
)
400 UNIMPLEMENTED_DBGBREAK();
405 This could be implemented much more intelligently by mapping instances
406 of a CoW zero page into the affected regions. We just RtlZeroMemory
412 CcZeroData(IN PFILE_OBJECT FileObject
,
413 IN PLARGE_INTEGER StartOffset
,
414 IN PLARGE_INTEGER EndOffset
,
417 PNOCC_BCB Bcb
= NULL
;
418 PLIST_ENTRY ListEntry
= NULL
;
419 LARGE_INTEGER LowerBound
= *StartOffset
;
420 LARGE_INTEGER UpperBound
= *EndOffset
;
421 LARGE_INTEGER Target
, End
;
422 PVOID PinnedBcb
, PinnedBuffer
;
423 PNOCC_CACHE_MAP Map
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
425 DPRINT("S %I64x E %I64x\n",
426 StartOffset
->QuadPart
,
427 EndOffset
->QuadPart
);
432 IO_STATUS_BLOCK IOSB
;
433 PCHAR ZeroBuf
= ExAllocatePool(PagedPool
, PAGE_SIZE
);
436 if (!ZeroBuf
) RtlRaiseStatus(STATUS_INSUFFICIENT_RESOURCES
);
437 DPRINT1("RtlZeroMemory(%x,%x)\n", ZeroBuf
, PAGE_SIZE
);
438 RtlZeroMemory(ZeroBuf
, PAGE_SIZE
);
440 Target
.QuadPart
= PAGE_ROUND_DOWN(LowerBound
.QuadPart
);
441 End
.QuadPart
= PAGE_ROUND_UP(UpperBound
.QuadPart
);
443 // Handle leading page
444 if (LowerBound
.QuadPart
!= Target
.QuadPart
)
446 ToWrite
= MIN(UpperBound
.QuadPart
- LowerBound
.QuadPart
,
447 (PAGE_SIZE
- LowerBound
.QuadPart
) & (PAGE_SIZE
- 1));
449 DPRINT("Zero last half %I64x %lx\n",
453 Status
= MiSimpleRead(FileObject
,
460 if (!NT_SUCCESS(Status
))
463 RtlRaiseStatus(Status
);
466 DPRINT1("RtlZeroMemory(%p, %lx)\n",
467 ZeroBuf
+ LowerBound
.QuadPart
- Target
.QuadPart
,
470 RtlZeroMemory(ZeroBuf
+ LowerBound
.QuadPart
- Target
.QuadPart
,
473 Status
= MiSimpleWrite(FileObject
,
477 UpperBound
.QuadPart
-Target
.QuadPart
),
480 if (!NT_SUCCESS(Status
))
483 RtlRaiseStatus(Status
);
485 Target
.QuadPart
+= PAGE_SIZE
;
488 DPRINT1("RtlZeroMemory(%x,%x)\n", ZeroBuf
, PAGE_SIZE
);
489 RtlZeroMemory(ZeroBuf
, PAGE_SIZE
);
491 while (UpperBound
.QuadPart
- Target
.QuadPart
> PAGE_SIZE
)
493 DPRINT("Zero full page %I64x\n",
496 Status
= MiSimpleWrite(FileObject
,
502 if (!NT_SUCCESS(Status
))
505 RtlRaiseStatus(Status
);
507 Target
.QuadPart
+= PAGE_SIZE
;
510 if (UpperBound
.QuadPart
> Target
.QuadPart
)
512 ToWrite
= UpperBound
.QuadPart
- Target
.QuadPart
;
513 DPRINT("Zero first half %I64x %lx\n",
517 Status
= MiSimpleRead(FileObject
,
524 if (!NT_SUCCESS(Status
))
527 RtlRaiseStatus(Status
);
529 DPRINT1("RtlZeroMemory(%x,%x)\n", ZeroBuf
, ToWrite
);
530 RtlZeroMemory(ZeroBuf
, ToWrite
);
531 Status
= MiSimpleWrite(FileObject
,
535 UpperBound
.QuadPart
-Target
.QuadPart
),
537 if (!NT_SUCCESS(Status
))
540 RtlRaiseStatus(Status
);
542 Target
.QuadPart
+= PAGE_SIZE
;
550 ListEntry
= Map
->AssociatedBcb
.Flink
;
552 while (ListEntry
!= &Map
->AssociatedBcb
)
554 Bcb
= CONTAINING_RECORD(ListEntry
, NOCC_BCB
, ThisFileList
);
555 CcpReferenceCache(Bcb
- CcCacheSections
);
557 if (Bcb
->FileOffset
.QuadPart
+ Bcb
->Length
>= LowerBound
.QuadPart
&&
558 Bcb
->FileOffset
.QuadPart
< UpperBound
.QuadPart
)
560 DPRINT("Bcb #%x (@%I64x)\n",
561 Bcb
- CcCacheSections
,
562 Bcb
->FileOffset
.QuadPart
);
564 Target
.QuadPart
= MAX(Bcb
->FileOffset
.QuadPart
,
565 LowerBound
.QuadPart
);
567 End
.QuadPart
= MIN(Map
->FileSizes
.ValidDataLength
.QuadPart
,
568 UpperBound
.QuadPart
);
570 End
.QuadPart
= MIN(End
.QuadPart
,
571 Bcb
->FileOffset
.QuadPart
+ Bcb
->Length
);
575 if (!CcPreparePinWrite(FileObject
,
577 End
.QuadPart
- Target
.QuadPart
,
586 ASSERT(PinnedBcb
== Bcb
);
589 ListEntry
= ListEntry
->Flink
;
590 /* Return from pin state */
591 CcpUnpinData(PinnedBcb
, TRUE
);
594 CcpUnpinData(Bcb
, TRUE
);
604 CcGetFileObjectFromSectionPtrs(IN PSECTION_OBJECT_POINTERS SectionObjectPointer
)
606 PFILE_OBJECT Result
= NULL
;
607 PNOCC_CACHE_MAP Map
= SectionObjectPointer
->SharedCacheMap
;
609 if (!IsListEmpty(&Map
->AssociatedBcb
))
611 PNOCC_BCB Bcb
= CONTAINING_RECORD(Map
->AssociatedBcb
.Flink
,
615 Result
= MmGetFileObjectForSection((PROS_SECTION_OBJECT
)Bcb
->SectionObject
);
623 CcGetFileObjectFromBcb(PVOID Bcb
)
625 PNOCC_BCB RealBcb
= (PNOCC_BCB
)Bcb
;
626 DPRINT("BCB #%x\n", RealBcb
- CcCacheSections
);
627 return MmGetFileObjectForSection((PROS_SECTION_OBJECT
)RealBcb
->SectionObject
);