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(&WorkItem
->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(&WorkItem
->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, %I64x:%d)\n",
491 FileOffset
->QuadPart
,
494 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL
);
496 Target
.HighPart
= FileOffset
->HighPart
;
497 Target
.LowPart
= CACHE_ROUND_DOWN(FileOffset
->LowPart
);
501 /* Find out if any range is a superset of what we want */
502 /* Find an accomodating section */
503 BcbHead
= CcpFindMatchingMap(&Map
->AssociatedBcb
, FileOffset
, Length
);
505 if (BcbHead
!= INVALID_CACHE
)
507 Bcb
= &CcCacheSections
[BcbHead
];
510 *Buffer
= ((PCHAR
)Bcb
->BaseAddress
) + (int)(FileOffset
->QuadPart
- Bcb
->FileOffset
.QuadPart
);
512 DPRINT("Bcb #%x Buffer maps (%I64x) At %x Length %x (Getting %p:%x) %wZ\n",
513 Bcb
- CcCacheSections
,
514 Bcb
->FileOffset
.QuadPart
,
519 &FileObject
->FileName
);
525 DPRINT("File size %I64x\n",
526 Map
->FileSizes
.ValidDataLength
.QuadPart
);
528 /* Not all files have length, in fact filesystems often use stream file
529 objects for various internal purposes and are loose about the file
530 length, since the filesystem promises itself to write the right number
531 of bytes to the internal stream. In these cases, we just allow the file
532 to have the full stripe worth of space. */
533 if (Map
->FileSizes
.ValidDataLength
.QuadPart
)
535 SectionSize
= min(CACHE_STRIPE
,
536 Map
->FileSizes
.ValidDataLength
.QuadPart
- Target
.QuadPart
);
540 SectionSize
= CACHE_STRIPE
;
543 DPRINT("Allocating a cache stripe at %x:%d\n",
544 Target
.LowPart
, SectionSize
);
546 //ASSERT(SectionSize <= CACHE_STRIPE);
549 /* CcpAllocateSection doesn't need the lock, so we'll give other action
551 Status
= CcpAllocateSection(FileObject
,
553 #ifdef PIN_WRITE_ONLY
561 if (!NT_SUCCESS(Status
))
565 DPRINT1("End %08x\n", Status
);
570 /* Returns a reference */
571 DPRINT("Allocating cache sections: %wZ\n", &FileObject
->FileName
);
572 BcbHead
= CcpAllocateCacheSections(FileObject
, SectionObject
);
573 /* XXX todo: we should handle the immediate fail case here, but don't */
574 if (BcbHead
== INVALID_CACHE
)
577 DbgPrint("Cache Map:");
578 for (i
= 0; i
< CACHE_NUM_SECTIONS
; i
++)
580 if (!(i
% 64)) DbgPrint("\n");
582 CcCacheSections
[i
].RefCount
+ (RtlTestBit(CcCacheBitmap
, i
) ? '@' : '`'));
586 KeWaitForSingleObject(&CcDeleteEvent
,
595 DPRINT("BcbHead #%x (final)\n", BcbHead
);
597 if (BcbHead
== INVALID_CACHE
)
605 DPRINT("Selected BCB #%x\n", BcbHead
);
606 ViewSize
= CACHE_STRIPE
;
608 Bcb
= &CcCacheSections
[BcbHead
];
609 /* MmMapCacheViewInSystemSpaceAtOffset is one of three methods of Mm
610 that are specific to NewCC. In this case, it's implementation
611 exactly mirrors MmMapViewInSystemSpace, but allows an offset to
613 Status
= MmMapCacheViewInSystemSpaceAtOffset(SectionObject
->Segment
,
618 /* Summary: Failure. Dereference our section and tell the user we failed */
619 if (!NT_SUCCESS(Status
))
623 ObDereferenceObject(SectionObject
);
624 RemoveEntryList(&Bcb
->ThisFileList
);
625 RtlZeroMemory(Bcb
, sizeof(*Bcb
));
626 RtlClearBit(CcCacheBitmap
, BcbHead
);
627 DPRINT1("Failed to map\n");
631 /* Summary: Success. Put together a valid Bcb and link it with the others
632 * in the NOCC_CACHE_MAP.
636 Bcb
->Length
= MIN(Map
->FileSizes
.ValidDataLength
.QuadPart
- Target
.QuadPart
,
639 Bcb
->SectionObject
= SectionObject
;
641 Bcb
->FileOffset
= Target
;
642 InsertTailList(&Map
->AssociatedBcb
, &Bcb
->ThisFileList
);
644 *BcbResult
= &CcCacheSections
[BcbHead
];
645 *Buffer
= ((PCHAR
)Bcb
->BaseAddress
) + (int)(FileOffset
->QuadPart
- Bcb
->FileOffset
.QuadPart
);
648 DPRINT("Bcb #%x Buffer maps (%I64x) At %x Length %x (Getting %p:%lx) %wZ\n",
649 Bcb
- CcCacheSections
,
650 Bcb
->FileOffset
.QuadPart
,
655 &FileObject
->FileName
);
657 EndInterval
.QuadPart
= Bcb
->FileOffset
.QuadPart
+ Bcb
->Length
- 1;
658 ASSERT((EndInterval
.QuadPart
& ~(CACHE_STRIPE
- 1)) ==
659 (Bcb
->FileOffset
.QuadPart
& ~(CACHE_STRIPE
- 1)));
667 /* Fault in the pages. This forces reads to happen now. */
669 PCHAR FaultIn
= Bcb
->BaseAddress
;
671 DPRINT("Faulting in pages at this point: file %wZ %I64x:%x\n",
672 &FileObject
->FileName
,
673 Bcb
->FileOffset
.QuadPart
,
676 for (i
= 0; i
< Bcb
->Length
; i
+= PAGE_SIZE
)
681 ASSERT(Bcb
>= CcCacheSections
&&
682 Bcb
< (CcCacheSections
+ CACHE_NUM_SECTIONS
));
694 CcMapData(IN PFILE_OBJECT FileObject
,
695 IN PLARGE_INTEGER FileOffset
,
698 OUT PVOID
*BcbResult
,
703 Result
= CcpMapData(FileObject
,
712 PNOCC_BCB Bcb
= (PNOCC_BCB
)*BcbResult
;
714 ASSERT(Bcb
>= CcCacheSections
&&
715 Bcb
< CcCacheSections
+ CACHE_NUM_SECTIONS
);
717 ASSERT(Bcb
->BaseAddress
);
719 CcpReferenceCache(Bcb
- CcCacheSections
);
726 /* Used by functions that repin data, CcpPinMappedData does not alter the map,
727 but finds the appropriate stripe and update the accounting. */
730 CcpPinMappedData(IN PNOCC_CACHE_MAP Map
,
731 IN PLARGE_INTEGER FileOffset
,
736 BOOLEAN Exclusive
= Flags
& PIN_EXCLUSIVE
;
742 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
));
743 BcbHead
= CcpFindMatchingMap(&Map
->AssociatedBcb
, FileOffset
, Length
);
744 if (BcbHead
== INVALID_CACHE
)
750 TheBcb
= &CcCacheSections
[BcbHead
];
754 DPRINT("Requesting #%x Exclusive\n", BcbHead
);
755 CcpMarkForExclusive(BcbHead
);
759 DPRINT("Reference #%x\n", BcbHead
);
760 CcpReferenceCache(BcbHead
);
764 CcpReferenceCacheExclusive(BcbHead
);
774 CcPinMappedData(IN PFILE_OBJECT FileObject
,
775 IN PLARGE_INTEGER FileOffset
,
781 PNOCC_CACHE_MAP Map
= (PNOCC_CACHE_MAP
)FileObject
->SectionObjectPointer
->SharedCacheMap
;
785 DPRINT1("Not cached\n");
789 if (CcpMapData(FileObject
, FileOffset
, Length
, Flags
, Bcb
, &Buffer
))
791 return CcpPinMappedData(Map
, FileOffset
, Length
, Flags
, Bcb
);
795 DPRINT1("could not map\n");
802 CcPinRead(IN PFILE_OBJECT FileObject
,
803 IN PLARGE_INTEGER FileOffset
,
812 Result
= CcPinMappedData(FileObject
, FileOffset
, Length
, Flags
, Bcb
);
818 *Buffer
= ((PCHAR
)RealBcb
->BaseAddress
) + (int)(FileOffset
->QuadPart
- RealBcb
->FileOffset
.QuadPart
);
827 CcPreparePinWrite(IN PFILE_OBJECT FileObject
,
828 IN PLARGE_INTEGER FileOffset
,
837 #ifdef PIN_WRITE_ONLY
839 SIZE_T NumberOfBytes
;
843 DPRINT("CcPreparePinWrite(%x:%x)\n", Buffer
, Length
);
845 Result
= CcPinRead(FileObject
, FileOffset
, Length
, Flags
, Bcb
, Buffer
);
852 #ifdef PIN_WRITE_ONLY
853 BaseAddress
= RealBcb
->BaseAddress
;
854 NumberOfBytes
= RealBcb
->Length
;
856 MiProtectVirtualMemory(NULL
,
864 RealBcb
->Dirty
= TRUE
;
868 DPRINT("Zero fill #%x %I64x:%x Buffer %x %wZ\n",
869 RealBcb
- CcCacheSections
,
870 FileOffset
->QuadPart
,
873 &FileObject
->FileName
);
875 DPRINT1("RtlZeroMemory(%p, %lx)\n", *Buffer
, Length
);
876 RtlZeroMemory(*Buffer
, Length
);
885 CcpUnpinData is the internal function that generally handles unpinning data.
886 It may be a little confusing, because of the way reference counts are handled.
888 A reference count of 2 or greater means that the stripe is still fully pinned
889 and can't be removed. If the owner had taken an exclusive reference, then
890 give one up. Note that it's an error to take more than one exclusive reference
891 or to take a non-exclusive reference after an exclusive reference, so detecting
892 or handling that case is not considered.
894 ReleaseBit is unset if we want to detect when a cache stripe would become
895 evictable without actually giving up our reference. We might want to do that
896 if we were going to flush before formally releasing the cache stripe, although
897 that facility is not used meaningfully at this time.
899 A reference count of exactly 1 means that the stripe could potentially be
900 reused, but could also be evicted for another mapping. In general, most
901 stripes should be in that state most of the time.
903 A reference count of zero means that the Bcb is completely unused. That's the
904 start state and the state of a Bcb formerly owned by a file that is
911 CcpUnpinData(IN PNOCC_BCB RealBcb
, BOOLEAN ReleaseBit
)
913 if (RealBcb
->RefCount
<= 2)
915 RealBcb
->Exclusive
= FALSE
;
916 if (RealBcb
->ExclusiveWaiter
)
918 DPRINT("Triggering exclusive waiter\n");
919 KeSetEvent(&RealBcb
->ExclusiveWait
, IO_NO_INCREMENT
, FALSE
);
923 if (RealBcb
->RefCount
== 2 && !ReleaseBit
)
925 if (RealBcb
->RefCount
> 1)
927 DPRINT("Removing one reference #%x\n", RealBcb
- CcCacheSections
);
929 KeSetEvent(&CcDeleteEvent
, IO_DISK_INCREMENT
, FALSE
);
931 if (RealBcb
->RefCount
== 1)
933 DPRINT("Clearing allocation bit #%x\n", RealBcb
- CcCacheSections
);
935 RtlClearBit(CcCacheBitmap
, RealBcb
- CcCacheSections
);
937 #ifdef PIN_WRITE_ONLY
938 PVOID BaseAddress
= RealBcb
->BaseAddress
;
939 SIZE_T NumberOfBytes
= RealBcb
->Length
;
942 MiProtectVirtualMemory(NULL
,
955 CcUnpinData(IN PVOID Bcb
)
957 PNOCC_BCB RealBcb
= (PNOCC_BCB
)Bcb
;
958 ULONG Selected
= RealBcb
- CcCacheSections
;
961 ASSERT(RealBcb
>= CcCacheSections
&&
962 RealBcb
- CcCacheSections
< CACHE_NUM_SECTIONS
);
964 DPRINT("CcUnpinData Bcb #%x (RefCount %d)\n", Selected
, RealBcb
->RefCount
);
967 Released
= CcpUnpinData(RealBcb
, FALSE
);
972 CcpUnpinData(RealBcb
, TRUE
);
979 CcSetBcbOwnerPointer(IN PVOID Bcb
,
980 IN PVOID OwnerPointer
)
982 PNOCC_BCB RealBcb
= (PNOCC_BCB
)Bcb
;
984 CcpReferenceCache(RealBcb
- CcCacheSections
);
985 RealBcb
->OwnerPointer
= OwnerPointer
;
991 CcUnpinDataForThread(IN PVOID Bcb
,
992 IN ERESOURCE_THREAD ResourceThreadId
)