2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/cache/pinsup.c
5 * PURPOSE: Logging and configuration routines
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
10 /* INCLUDES *******************************************************************/
14 #include "section/newmm.h"
18 /* The following is a test mode that only works with modified filesystems.
19 * it maps the cache sections read only until they're pinned writable, and then
20 * turns them readonly again when they're unpinned.
21 * This helped me determine that a certain bug was not a memory overwrite. */
23 //#define PIN_WRITE_ONLY
27 Pinsup implements the core of NewCC.
29 A couple of things about this code:
31 I wrote this code over the course of about 2 years, often referring to Rajeev
32 Nagar's Filesystem Internals, book, the msdn pages on the Cc interface, and
33 a few NT filesystems that are open sourced. I went to fairly great lengths to
34 achieve a couple of goals.
36 1) To make a strictly layered facility that relies entirely on Mm to provide
37 maps. There were many ways in which data segments in the legacy Mm were unable
38 to provide what I needed; page maps were only 4 gig, and all offsets were in
39 ULONG, so no mapping at an offset greater than 4 gig was possible. Worse than
40 that, due to a convoluted set of dependencies, it would have been impossible to
41 support any two mappings farther apart than 4 gig, even if the above was
42 corrected. Along with that, the cache system's ownership of some pages was
43 integral to the operation of legacy Mm. All of the above problems, along with
44 an ambiguity about when the size of a file for mapping purposes is acquired,
45 and its inability to allow a file to be resized when any mappings were active
46 led me to rewrite data sections (and all other kinds of sections in the
47 original version), and use that layer to implement the Cc API without regard
48 to any internal, undocumented parts.
50 2) To write the simplest possible code that implements the Cc interface as
51 documented. Again this is without regard to any information that might be
52 gained through reverse engineering the real Cc. All conclusions about workings
53 of Cc here are mine, any failures are mine, any differences to the documented
54 interface were introduced by me due to misreading, misunderstanding or mis
55 remembering while implementing the code. I also implemented some obvious, but
56 not actually specified behaviors of Cc, for example that each cache stripe is
57 represented by a distinct BCB that the user can make decisions about as an
60 3) To make real filesystems work properly.
62 So about how it works:
64 CcCacheSections is the collection of cache sections that are currently mapped.
65 The cache ranges which are allocated and contain pages is larger, due to the
66 addition of sections containing rmaps and page references, but this array
67 determines the actual mapped pages on behalf of all mapped files for Cc's use.
68 All BCB pointers yielded to a driver are a pointer to one of these cache stripe
69 structures. The data structure is specified as opaque and so it contains
70 information convenient to NEWCC's implementation here. Free entries are
71 summarized in CcpBitmapBuffer, for which bits are set when the entry may be
72 safely evicted and redirected for use by another client. Note that the
73 reference count for an evictable cache section will generally be 1, since
74 we'll keep a reference to wait for any subsequent mapping of the same stripe.
75 We use CcCacheClockHand as a hint to start checking free bits at a point that
76 walks around the cache stripe list, so that we might evict a different stripe
77 every time even if all are awaiting reuse. This is a way to avoid thrashing.
79 CcpBitmapBuffer is the RTL_BITMAP that allows us to quickly decide what buffer
80 to allocate from the mapped buffer set.
82 CcDeleteEvent is an event used to wait for a cache stripe reference count to
83 go to 1, thus making the stripe eligible for eviction. It's used by CcpMapData
84 to wait for a free map when we can't fail.
86 All in all, use of Mm by Cc makes this code into a simple manager that wields
87 sections on behalf of filesystems. As such, its code is fairly high level and
88 no architecture specific changes should be necessary.
92 /* GLOBALS ********************************************************************/
94 #define TAG_MAP_SEC TAG('C', 'c', 'S', 'x')
95 #define TAG_MAP_READ TAG('M', 'c', 'p', 'y')
96 #define TAG_MAP_BCB TAG('B', 'c', 'b', ' ')
98 NOCC_BCB CcCacheSections
[CACHE_NUM_SECTIONS
];
99 CHAR CcpBitmapBuffer
[sizeof(RTL_BITMAP
) + ROUND_UP((CACHE_NUM_SECTIONS
), 32) / 8];
100 PRTL_BITMAP CcCacheBitmap
= (PRTL_BITMAP
)&CcpBitmapBuffer
;
102 KEVENT CcDeleteEvent
;
103 KEVENT CcFinalizeEvent
;
104 ULONG CcCacheClockHand
;
105 LONG CcOutstandingDeletes
;
107 /* FUNCTIONS ******************************************************************/
112 _CcpLock(const char *file
,
115 //DPRINT("<<<---<<< CC In Mutex(%s:%d %x)!\n", file, line, PsGetCurrentThread());
116 ExAcquireFastMutex(&CcMutex
);
120 _CcpUnlock(const char *file
,
123 ExReleaseFastMutex(&CcMutex
);
124 //DPRINT(">>>--->>> CC Exit Mutex!\n", file, line);
129 MmGetDeviceObjectForFile(IN PFILE_OBJECT FileObject
);
133 Allocate an almost ordinary section object for use by the cache system.
134 The special internal SEC_CACHE flag is used to indicate that the section
135 should not count when determining whether the file can be resized.
140 CcpAllocateSection(PFILE_OBJECT FileObject
,
143 PROS_SECTION_OBJECT
*Result
)
146 LARGE_INTEGER MaxSize
;
148 MaxSize
.QuadPart
= Length
;
150 DPRINT("Making Section for File %x\n", FileObject
);
151 DPRINT("File name %wZ\n", &FileObject
->FileName
);
153 Status
= MmCreateSection((PVOID
*)Result
,
154 STANDARD_RIGHTS_REQUIRED
,
158 SEC_RESERVE
| SEC_CACHE
,
165 typedef struct _WORK_QUEUE_WITH_CONTEXT
167 WORK_QUEUE_ITEM WorkItem
;
169 LARGE_INTEGER FileOffset
;
170 LARGE_INTEGER MapSize
;
171 PROS_SECTION_OBJECT ToDeref
;
172 PACQUIRE_FOR_LAZY_WRITE AcquireForLazyWrite
;
173 PRELEASE_FROM_LAZY_WRITE ReleaseFromLazyWrite
;
176 } WORK_QUEUE_WITH_CONTEXT
, *PWORK_QUEUE_WITH_CONTEXT
;
180 Unmap a cache stripe. Note that cache stripes aren't unmapped when their
181 last reference disappears. We enter this code only if cache for the file
182 is uninitialized in the last file object, or a cache stripe is evicted.
187 CcpUnmapCache(PVOID Context
)
189 PWORK_QUEUE_WITH_CONTEXT WorkItem
= (PWORK_QUEUE_WITH_CONTEXT
)Context
;
190 DPRINT("Unmapping (finally) %x\n", WorkItem
->ToUnmap
);
191 MmUnmapCacheViewInSystemSpace(WorkItem
->ToUnmap
);
192 ObDereferenceObject(WorkItem
->ToDeref
);
193 ExFreePool(WorkItem
);
199 Somewhat deceptively named function which removes the last reference to a
200 cache stripe and completely removes it using CcUnmapCache. This may be
201 done either inline (if the Immediate BOOLEAN is set), or using a work item
202 at a later time. Whether this is called to unmap immeidately is mainly
203 determined by whether the caller is calling from a place in filesystem code
204 where a deadlock may occur if immediate flushing is required.
206 It's always safe to reuse the Bcb at CcCacheSections[Start] after calling
211 /* Must have acquired the mutex */
213 CcpDereferenceCache(ULONG Start
,
219 LARGE_INTEGER MappedSize
;
220 LARGE_INTEGER BaseOffset
;
221 PWORK_QUEUE_WITH_CONTEXT WorkItem
;
223 DPRINT("CcpDereferenceCache(#%x)\n", Start
);
225 Bcb
= &CcCacheSections
[Start
];
228 ToUnmap
= Bcb
->BaseAddress
;
229 BaseOffset
= Bcb
->FileOffset
;
230 MappedSize
= Bcb
->Map
->FileSizes
.ValidDataLength
;
232 DPRINT("Dereference #%x (count %d)\n", Start
, Bcb
->RefCount
);
233 ASSERT(Bcb
->SectionObject
);
234 ASSERT(Bcb
->RefCount
== 1);
236 DPRINT("Firing work item for %x\n", Bcb
->BaseAddress
);
241 MiFlushMappedSection(ToUnmap
, &BaseOffset
, &MappedSize
, Dirty
);
248 PROS_SECTION_OBJECT ToDeref
= Bcb
->SectionObject
;
250 Bcb
->SectionObject
= NULL
;
251 Bcb
->BaseAddress
= NULL
;
252 Bcb
->FileOffset
.QuadPart
= 0;
256 RemoveEntryList(&Bcb
->ThisFileList
);
259 MmUnmapCacheViewInSystemSpace(ToUnmap
);
260 ObDereferenceObject(ToDeref
);
265 WorkItem
= ExAllocatePool(NonPagedPool
, sizeof(*WorkItem
));
266 if (!WorkItem
) KeBugCheck(0);
267 WorkItem
->ToUnmap
= Bcb
->BaseAddress
;
268 WorkItem
->FileOffset
= Bcb
->FileOffset
;
269 WorkItem
->Dirty
= Bcb
->Dirty
;
270 WorkItem
->MapSize
= MappedSize
;
271 WorkItem
->ToDeref
= Bcb
->SectionObject
;
272 WorkItem
->AcquireForLazyWrite
= Bcb
->Map
->Callbacks
.AcquireForLazyWrite
;
273 WorkItem
->ReleaseFromLazyWrite
= Bcb
->Map
->Callbacks
.ReleaseFromLazyWrite
;
274 WorkItem
->LazyContext
= Bcb
->Map
->LazyContext
;
276 ExInitializeWorkItem(((PWORK_QUEUE_ITEM
)WorkItem
),
277 (PWORKER_THREAD_ROUTINE
)CcpUnmapCache
,
281 Bcb
->SectionObject
= NULL
;
282 Bcb
->BaseAddress
= NULL
;
283 Bcb
->FileOffset
.QuadPart
= 0;
287 RemoveEntryList(&Bcb
->ThisFileList
);
290 ExQueueWorkItem((PWORK_QUEUE_ITEM
)WorkItem
, DelayedWorkQueue
);
298 CcpAllocateCacheSections is called by CcpMapData to obtain a cache stripe,
299 possibly evicting an old stripe by calling CcpDereferenceCache in order to
302 This function was named plural due to a question I had at the beginning of
303 this endeavor about whether a map may span a 256k stripe boundary. It can't
304 so this function can only return the index of one Bcb. Returns INVALID_CACHE
310 CcpAllocateCacheSections(PFILE_OBJECT FileObject
,
311 PROS_SECTION_OBJECT SectionObject
)
313 ULONG i
= INVALID_CACHE
;
317 DPRINT("AllocateCacheSections: FileObject %x\n", FileObject
);
319 if (!FileObject
->SectionObjectPointer
)
320 return INVALID_CACHE
;
322 Map
= (PNOCC_CACHE_MAP
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
325 return INVALID_CACHE
;
327 DPRINT("Allocating Cache Section\n");
329 i
= RtlFindClearBitsAndSet(CcCacheBitmap
, 1, CcCacheClockHand
);
330 CcCacheClockHand
= (i
+ 1) % CACHE_NUM_SECTIONS
;
332 if (i
!= INVALID_CACHE
)
334 DPRINT("Setting up Bcb #%x\n", i
);
336 Bcb
= &CcCacheSections
[i
];
338 ASSERT(Bcb
->RefCount
< 2);
340 if (Bcb
->RefCount
> 0)
342 CcpDereferenceCache(i
, FALSE
);
345 ASSERT(!Bcb
->RefCount
);
348 DPRINT("Bcb #%x RefCount %d\n", Bcb
- CcCacheSections
, Bcb
->RefCount
);
350 if (!RtlTestBit(CcCacheBitmap
, i
))
352 DPRINT1("Somebody stoeled BCB #%x\n", i
);
354 ASSERT(RtlTestBit(CcCacheBitmap
, i
));
356 DPRINT("Allocated #%x\n", i
);
357 ASSERT(CcCacheSections
[i
].RefCount
);
361 DPRINT1("Failed to allocate cache segment\n");
366 /* Must have acquired the mutex */
368 CcpReferenceCache(ULONG Start
)
371 Bcb
= &CcCacheSections
[Start
];
372 ASSERT(Bcb
->SectionObject
);
374 RtlSetBit(CcCacheBitmap
, Start
);
379 CcpMarkForExclusive(ULONG Start
)
382 Bcb
= &CcCacheSections
[Start
];
383 Bcb
->ExclusiveWaiter
++;
388 Cache stripes have an idea of exclusive access, which would be hard to support
389 properly in the previous code. In our case, it's fairly easy, since we have
390 an event that indicates that the previous exclusive waiter has returned in each
394 /* Must not have the mutex */
396 CcpReferenceCacheExclusive(ULONG Start
)
398 PNOCC_BCB Bcb
= &CcCacheSections
[Start
];
400 KeWaitForSingleObject(&Bcb
->ExclusiveWait
,
407 ASSERT(Bcb
->ExclusiveWaiter
);
408 ASSERT(Bcb
->SectionObject
);
409 Bcb
->Exclusive
= TRUE
;
410 Bcb
->ExclusiveWaiter
--;
411 RtlSetBit(CcCacheBitmap
, Start
);
417 Find a map that encompasses the target range. This function does not check
418 whether the desired range is partly outside the stripe. This could be
419 implemented with a generic table, but we generally aren't carring around a lot
420 of segments at once for a particular file.
422 When this returns a map for a given file address, then that address is by
423 definition already mapped and can be operated on.
425 Returns a valid index or INVALID_CACHE.
428 /* Must have the mutex */
430 CcpFindMatchingMap(PLIST_ENTRY Head
,
431 PLARGE_INTEGER FileOffset
,
435 //DPRINT("Find Matching Map: (%x) %x:%x\n", FileOffset->LowPart, Length);
436 for (Entry
= Head
->Flink
; Entry
!= Head
; Entry
= Entry
->Flink
)
438 //DPRINT("Link @%x\n", Entry);
439 PNOCC_BCB Bcb
= CONTAINING_RECORD(Entry
, NOCC_BCB
, ThisFileList
);
440 //DPRINT("Selected BCB %x #%x\n", Bcb, Bcb - CcCacheSections);
441 //DPRINT("This File: %x:%x\n", Bcb->FileOffset.LowPart, Bcb->Length);
442 if (FileOffset
->QuadPart
>= Bcb
->FileOffset
.QuadPart
&&
443 FileOffset
->QuadPart
< Bcb
->FileOffset
.QuadPart
+ CACHE_STRIPE
)
445 //DPRINT("Found match at #%x\n", Bcb - CcCacheSections);
446 return Bcb
- CcCacheSections
;
450 //DPRINT("This region isn't mapped\n");
452 return INVALID_CACHE
;
457 Internal function that's used by all pinning functions.
458 It causes a mapped region to exist and prefaults the pages in it if possible,
459 possibly evicting another stripe in order to get our stripe.
465 CcpMapData(IN PFILE_OBJECT FileObject
,
466 IN PLARGE_INTEGER FileOffset
,
469 OUT PVOID
*BcbResult
,
472 BOOLEAN Success
= FALSE
, FaultIn
= FALSE
;
473 /* Note: windows 2000 drivers treat this as a bool */
474 //BOOLEAN Wait = (Flags & MAP_WAIT) || (Flags == TRUE);
475 LARGE_INTEGER Target
, EndInterval
;
476 ULONG BcbHead
, SectionSize
, ViewSize
;
477 PNOCC_BCB Bcb
= NULL
;
478 PROS_SECTION_OBJECT SectionObject
= NULL
;
480 PNOCC_CACHE_MAP Map
= (PNOCC_CACHE_MAP
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
481 ViewSize
= CACHE_STRIPE
;
485 DPRINT1("File object was not mapped\n");
489 DPRINT("CcMapData(F->%x,%08x%08x:%d)\n",
491 FileOffset
->HighPart
,
495 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL
);
497 Target
.HighPart
= FileOffset
->HighPart
;
498 Target
.LowPart
= CACHE_ROUND_DOWN(FileOffset
->LowPart
);
502 /* Find out if any range is a superset of what we want */
503 /* Find an accomodating section */
504 BcbHead
= CcpFindMatchingMap(&Map
->AssociatedBcb
, FileOffset
, Length
);
506 if (BcbHead
!= INVALID_CACHE
)
508 Bcb
= &CcCacheSections
[BcbHead
];
511 *Buffer
= ((PCHAR
)Bcb
->BaseAddress
) + (int)(FileOffset
->QuadPart
- Bcb
->FileOffset
.QuadPart
);
513 DPRINT("Bcb #%x Buffer maps (%08x%08x) At %x Length %x (Getting %x:%x) %wZ\n",
514 Bcb
- CcCacheSections
,
515 Bcb
->FileOffset
.HighPart
,
516 Bcb
->FileOffset
.LowPart
,
521 &FileObject
->FileName
);
527 DPRINT("File size %08x%08x\n",
528 Map
->FileSizes
.ValidDataLength
.HighPart
,
529 Map
->FileSizes
.ValidDataLength
.LowPart
);
531 /* Not all files have length, in fact filesystems often use stream file
532 objects for various internal purposes and are loose about the file
533 length, since the filesystem promises itself to write the right number
534 of bytes to the internal stream. In these cases, we just allow the file
535 to have the full stripe worth of space. */
536 if (Map
->FileSizes
.ValidDataLength
.QuadPart
)
538 SectionSize
= min(CACHE_STRIPE
,
539 Map
->FileSizes
.ValidDataLength
.QuadPart
- Target
.QuadPart
);
543 SectionSize
= CACHE_STRIPE
;
546 DPRINT("Allocating a cache stripe at %x:%d\n",
547 Target
.LowPart
, SectionSize
);
549 //ASSERT(SectionSize <= CACHE_STRIPE);
552 /* CcpAllocateSection doesn't need the lock, so we'll give other action
554 Status
= CcpAllocateSection(FileObject
,
556 #ifdef PIN_WRITE_ONLY
564 if (!NT_SUCCESS(Status
))
568 DPRINT1("End %08x\n", Status
);
573 /* Returns a reference */
574 DPRINT("Allocating cache sections: %wZ\n", &FileObject
->FileName
);
575 BcbHead
= CcpAllocateCacheSections(FileObject
, SectionObject
);
576 /* XXX todo: we should handle the immediate fail case here, but don't */
577 if (BcbHead
== INVALID_CACHE
)
580 DbgPrint("Cache Map:");
581 for (i
= 0; i
< CACHE_NUM_SECTIONS
; i
++)
583 if (!(i
% 64)) DbgPrint("\n");
585 CcCacheSections
[i
].RefCount
+ (RtlTestBit(CcCacheBitmap
, i
) ? '@' : '`'));
589 KeWaitForSingleObject(&CcDeleteEvent
,
598 DPRINT("BcbHead #%x (final)\n", BcbHead
);
600 if (BcbHead
== INVALID_CACHE
)
608 DPRINT("Selected BCB #%x\n", BcbHead
);
609 ViewSize
= CACHE_STRIPE
;
611 Bcb
= &CcCacheSections
[BcbHead
];
612 /* MmMapCacheViewInSystemSpaceAtOffset is one of three methods of Mm
613 that are specific to NewCC. In this case, it's implementation
614 exactly mirrors MmMapViewInSystemSpace, but allows an offset to
616 Status
= MmMapCacheViewInSystemSpaceAtOffset(SectionObject
->Segment
,
621 /* Summary: Failure. Dereference our section and tell the user we failed */
622 if (!NT_SUCCESS(Status
))
626 ObDereferenceObject(SectionObject
);
627 RemoveEntryList(&Bcb
->ThisFileList
);
628 RtlZeroMemory(Bcb
, sizeof(*Bcb
));
629 RtlClearBit(CcCacheBitmap
, BcbHead
);
630 DPRINT1("Failed to map\n");
634 /* Summary: Success. Put together a valid Bcb and link it with the others
635 * in the NOCC_CACHE_MAP.
640 Bcb
->Length
= MIN(Map
->FileSizes
.ValidDataLength
.QuadPart
- Target
.QuadPart
,
643 Bcb
->SectionObject
= SectionObject
;
645 Bcb
->FileOffset
= Target
;
646 InsertTailList(&Map
->AssociatedBcb
, &Bcb
->ThisFileList
);
648 *BcbResult
= &CcCacheSections
[BcbHead
];
649 *Buffer
= ((PCHAR
)Bcb
->BaseAddress
) + (int)(FileOffset
->QuadPart
- Bcb
->FileOffset
.QuadPart
);
652 DPRINT("Bcb #%x Buffer maps (%08x%08x) At %x Length %x (Getting %x:%x) %wZ\n",
653 Bcb
- CcCacheSections
,
654 Bcb
->FileOffset
.HighPart
,
655 Bcb
->FileOffset
.LowPart
,
660 &FileObject
->FileName
);
662 EndInterval
.QuadPart
= Bcb
->FileOffset
.QuadPart
+ Bcb
->Length
- 1;
663 ASSERT((EndInterval
.QuadPart
& ~(CACHE_STRIPE
- 1)) ==
664 (Bcb
->FileOffset
.QuadPart
& ~(CACHE_STRIPE
- 1)));
674 /* Fault in the pages. This forces reads to happen now. */
676 PCHAR FaultIn
= Bcb
->BaseAddress
;
678 DPRINT("Faulting in pages at this point: file %wZ %08x%08x:%x\n",
679 &FileObject
->FileName
,
680 Bcb
->FileOffset
.HighPart
,
681 Bcb
->FileOffset
.LowPart
,
684 for (i
= 0; i
< Bcb
->Length
; i
+= PAGE_SIZE
)
689 ASSERT(Bcb
>= CcCacheSections
&&
690 Bcb
< (CcCacheSections
+ CACHE_NUM_SECTIONS
));
702 CcMapData(IN PFILE_OBJECT FileObject
,
703 IN PLARGE_INTEGER FileOffset
,
706 OUT PVOID
*BcbResult
,
711 Result
= CcpMapData(FileObject
,
720 PNOCC_BCB Bcb
= (PNOCC_BCB
)*BcbResult
;
722 ASSERT(Bcb
>= CcCacheSections
&&
723 Bcb
< CcCacheSections
+ CACHE_NUM_SECTIONS
);
725 ASSERT(Bcb
->BaseAddress
);
727 CcpReferenceCache(Bcb
- CcCacheSections
);
734 /* Used by functions that repin data, CcpPinMappedData does not alter the map,
735 but finds the appropriate stripe and update the accounting. */
738 CcpPinMappedData(IN PNOCC_CACHE_MAP Map
,
739 IN PLARGE_INTEGER FileOffset
,
744 BOOLEAN Exclusive
= Flags
& PIN_EXCLUSIVE
;
750 ASSERT(Map
->AssociatedBcb
.Flink
== &Map
->AssociatedBcb
|| (CONTAINING_RECORD(Map
->AssociatedBcb
.Flink
, NOCC_BCB
, ThisFileList
) >= CcCacheSections
&& CONTAINING_RECORD(Map
->AssociatedBcb
.Flink
, NOCC_BCB
, ThisFileList
) < CcCacheSections
+ CACHE_NUM_SECTIONS
));
751 BcbHead
= CcpFindMatchingMap(&Map
->AssociatedBcb
, FileOffset
, Length
);
752 if (BcbHead
== INVALID_CACHE
)
758 TheBcb
= &CcCacheSections
[BcbHead
];
762 DPRINT("Requesting #%x Exclusive\n", BcbHead
);
763 CcpMarkForExclusive(BcbHead
);
767 DPRINT("Reference #%x\n", BcbHead
);
768 CcpReferenceCache(BcbHead
);
772 CcpReferenceCacheExclusive(BcbHead
);
782 CcPinMappedData(IN PFILE_OBJECT FileObject
,
783 IN PLARGE_INTEGER FileOffset
,
789 PNOCC_CACHE_MAP Map
= (PNOCC_CACHE_MAP
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
793 DPRINT1("Not cached\n");
797 if (CcpMapData(FileObject
, FileOffset
, Length
, Flags
, Bcb
, &Buffer
))
799 return CcpPinMappedData(Map
, FileOffset
, Length
, Flags
, Bcb
);
803 DPRINT1("could not map\n");
810 CcPinRead(IN PFILE_OBJECT FileObject
,
811 IN PLARGE_INTEGER FileOffset
,
820 Result
= CcPinMappedData(FileObject
, FileOffset
, Length
, Flags
, Bcb
);
826 *Buffer
= ((PCHAR
)RealBcb
->BaseAddress
) + (int)(FileOffset
->QuadPart
- RealBcb
->FileOffset
.QuadPart
);
835 CcPreparePinWrite(IN PFILE_OBJECT FileObject
,
836 IN PLARGE_INTEGER FileOffset
,
845 #ifdef PIN_WRITE_ONLY
847 SIZE_T NumberOfBytes
;
851 DPRINT("CcPreparePinWrite(%x:%x)\n", Buffer
, Length
);
853 Result
= CcPinRead(FileObject
, FileOffset
, Length
, Flags
, Bcb
, Buffer
);
860 #ifdef PIN_WRITE_ONLY
861 BaseAddress
= RealBcb
->BaseAddress
;
862 NumberOfBytes
= RealBcb
->Length
;
864 MiProtectVirtualMemory(NULL
,
872 RealBcb
->Dirty
= TRUE
;
876 DPRINT("Zero fill #%x %08x%08x:%x Buffer %x %wZ\n",
877 RealBcb
- CcCacheSections
,
878 FileOffset
->u
.HighPart
,
879 FileOffset
->u
.LowPart
,
882 &FileObject
->FileName
);
884 DPRINT1("RtlZeroMemory(%x,%x)\n", *Buffer
, Length
);
885 RtlZeroMemory(*Buffer
, Length
);
894 CcpUnpinData is the internal function that generally handles unpinning data.
895 It may be a little confusing, because of the way reference counts are handled.
897 A reference count of 2 or greater means that the stripe is still fully pinned
898 and can't be removed. If the owner had taken an exclusive reference, then
899 give one up. Note that it's an error to take more than one exclusive reference
900 or to take a non-exclusive reference after an exclusive reference, so detecting
901 or handling that case is not considered.
903 ReleaseBit is unset if we want to detect when a cache stripe would become
904 evictable without actually giving up our reference. We might want to do that
905 if we were going to flush before formally releasing the cache stripe, although
906 that facility is not used meaningfully at this time.
908 A reference count of exactly 1 means that the stripe could potentially be
909 reused, but could also be evicted for another mapping. In general, most
910 stripes should be in that state most of the time.
912 A reference count of zero means that the Bcb is completely unused. That's the
913 start state and the state of a Bcb formerly owned by a file that is
920 CcpUnpinData(IN PNOCC_BCB RealBcb
, BOOLEAN ReleaseBit
)
922 if (RealBcb
->RefCount
<= 2)
924 RealBcb
->Exclusive
= FALSE
;
925 if (RealBcb
->ExclusiveWaiter
)
927 DPRINT("Triggering exclusive waiter\n");
928 KeSetEvent(&RealBcb
->ExclusiveWait
, IO_NO_INCREMENT
, FALSE
);
932 if (RealBcb
->RefCount
== 2 && !ReleaseBit
)
934 if (RealBcb
->RefCount
> 1)
936 DPRINT("Removing one reference #%x\n", RealBcb
- CcCacheSections
);
938 KeSetEvent(&CcDeleteEvent
, IO_DISK_INCREMENT
, FALSE
);
940 if (RealBcb
->RefCount
== 1)
942 DPRINT("Clearing allocation bit #%x\n", RealBcb
- CcCacheSections
);
944 RtlClearBit(CcCacheBitmap
, RealBcb
- CcCacheSections
);
946 #ifdef PIN_WRITE_ONLY
947 PVOID BaseAddress
= RealBcb
->BaseAddress
;
948 SIZE_T NumberOfBytes
= RealBcb
->Length
;
951 MiProtectVirtualMemory(NULL
,
964 CcUnpinData(IN PVOID Bcb
)
966 PNOCC_BCB RealBcb
= (PNOCC_BCB
)Bcb
;
967 ULONG Selected
= RealBcb
- CcCacheSections
;
970 ASSERT(RealBcb
>= CcCacheSections
&&
971 RealBcb
- CcCacheSections
< CACHE_NUM_SECTIONS
);
973 DPRINT("CcUnpinData Bcb #%x (RefCount %d)\n", Selected
, RealBcb
->RefCount
);
976 Released
= CcpUnpinData(RealBcb
, FALSE
);
981 CcpUnpinData(RealBcb
, TRUE
);
988 CcSetBcbOwnerPointer(IN PVOID Bcb
,
989 IN PVOID OwnerPointer
)
991 PNOCC_BCB RealBcb
= (PNOCC_BCB
)Bcb
;
993 CcpReferenceCache(RealBcb
- CcCacheSections
);
994 RealBcb
->OwnerPointer
= OwnerPointer
;
1000 CcUnpinDataForThread(IN PVOID Bcb
,
1001 IN ERESOURCE_THREAD ResourceThreadId
)