diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/cache/pinsup.c | 1274 |
1 files changed, 1274 insertions, 0 deletions
diff --git a/private/ntos/cache/pinsup.c b/private/ntos/cache/pinsup.c new file mode 100644 index 000000000..142806b1a --- /dev/null +++ b/private/ntos/cache/pinsup.c @@ -0,0 +1,1274 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + pinsup.c + +Abstract: + + This module implements the pointer-based Pin support routines for the + Cache subsystem. + +Author: + + Tom Miller [TomM] 4-June-1990 + +Revision History: + +--*/ + +#include "cc.h" + +// +// Define our debug constant +// + +#define me 0x00000008 + +#if LIST_DBG + +#define SetCallersAddress(BCB) { \ + RtlGetCallersAddress( &(BCB)->CallerAddress, \ + &(BCB)->CallersCallerAddress ); \ +} + +#endif + +// +// Internal routines +// + +POBCB +CcAllocateObcb ( + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN PBCB FirstBcb + ); + + +BOOLEAN +CcMapData ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + OUT PVOID *Bcb, + OUT PVOID *Buffer + ) + +/*++ + +Routine Description: + + This routine attempts to map the specified file data in the cache. + A pointer is returned to the desired data in the cache. + + If the caller does not want to block on this call, then + Wait should be supplied as FALSE. If Wait was supplied as FALSE and + it is currently impossible to supply the requested data without + blocking, then this routine will return FALSE. However, if the + data is immediately accessible in the cache and no blocking is + required, this routine returns TRUE with a pointer to the data. + + Note that a call to this routine with Wait supplied as TRUE is + considerably faster than a call with Wait supplies as FALSE, because + in the Wait TRUE case we only have to make sure the data is mapped + in order to return. + + It is illegal to modify data that is only mapped, and can in fact lead + to serious problems. It is impossible to check for this in all cases, + however CcSetDirtyPinnedData may implement some Assertions to check for + this. If the caller wishes to modify data that it has only mapped, then + it must *first* call CcPinMappedData. + + In any case, the caller MUST subsequently call CcUnpinData. + Naturally if CcPinRead or CcPreparePinWrite were called multiple + times for the same data, CcUnpinData must be called the same number + of times. + + The returned Buffer pointer is valid until the data is unpinned, at + which point it is invalid to use the pointer further. This buffer pointer + will remain valid if CcPinMappedData is called. + + Note that under some circumstances (like Wait supplied as FALSE or more + than a page is requested), this routine may actually pin the data, however + it is not necessary, and in fact not correct, for the caller to be concerned + about this. + +Arguments: + + FileObject - Pointer to the file object for a file which was + opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for + which CcInitializeCacheMap was called by the file system. + + FileOffset - Byte offset in file for desired data. + + Length - Length of desired data in bytes. + + Wait - FALSE if caller may not block, TRUE otherwise (see description + above) + + Bcb - On the first call this returns a pointer to a Bcb + parameter which must be supplied as input on all subsequent + calls, for this buffer + + Buffer - Returns pointer to desired data, valid until the buffer is + unpinned or freed. This pointer will remain valid if CcPinMappedData + is called. + +Return Value: + + FALSE - if Wait was supplied as FALSE and the data was not delivered + + TRUE - if the data is being delivered + +--*/ + +{ + PSHARED_CACHE_MAP SharedCacheMap; + LARGE_INTEGER BeyondLastByte; + ULONG ReceivedLength; + ULONG SavedState; + volatile UCHAR ch; + ULONG PageCount = COMPUTE_PAGES_SPANNED(((PVOID)FileOffset->LowPart), Length); + PETHREAD Thread = PsGetCurrentThread(); + + DebugTrace(+1, me, "CcMapData\n", 0 ); + + MmSavePageFaultReadAhead( Thread, &SavedState ); + + // + // Increment performance counters + // + + if (Wait) { + + CcMapDataWait += 1; + + // + // Initialize the indirect pointer to our miss counter. + // + + CcMissCounter = &CcMapDataWaitMiss; + + } else { + CcMapDataNoWait += 1; + } + + // + // Get pointer to SharedCacheMap. + // + + SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer + + sizeof(PVOID)); + + // + // Call local routine to Map or Access the file data. If we cannot map + // the data because of a Wait condition, return FALSE. + // + + if (Wait) { + + *Buffer = CcGetVirtualAddress( SharedCacheMap, + *FileOffset, + (PVACB *)Bcb, + &ReceivedLength ); + + ASSERT( ReceivedLength >= Length ); + + } else if (!CcPinFileData( FileObject, + FileOffset, + Length, + TRUE, + FALSE, + Wait, + (PBCB *)Bcb, + Buffer, + &BeyondLastByte )) { + + DebugTrace(-1, me, "CcMapData -> FALSE\n", 0 ); + + CcMapDataNoWaitMiss += 1; + + return FALSE; + + } else { + + ASSERT( (BeyondLastByte.QuadPart - FileOffset->QuadPart) >= Length ); + +#if LIST_DBG + { + KIRQL OldIrql; + PBCB BcbTemp = (PBCB)*Bcb; + + ExAcquireSpinLock( &CcBcbSpinLock, &OldIrql ); + + if (BcbTemp->CcBcbLinks.Flink == NULL) { + + InsertTailList( &CcBcbList, &BcbTemp->CcBcbLinks ); + CcBcbCount += 1; + ExReleaseSpinLock( &CcBcbSpinLock, OldIrql ); + SetCallersAddress( BcbTemp ); + + } else { + ExReleaseSpinLock( &CcBcbSpinLock, OldIrql ); + } + + } +#endif + + } + + // + // Now let's just sit here and take the miss(es) like a man (and count them). + // + + try { + + // + // Loop to touch each page + // + + BeyondLastByte.LowPart = 0; + + while (PageCount != 0) { + + MmSetPageFaultReadAhead( Thread, PageCount - 1 ); + + ch = *((volatile UCHAR *)(*Buffer) + BeyondLastByte.LowPart); + + BeyondLastByte.LowPart += PAGE_SIZE; + PageCount -= 1; + } + + } finally { + + MmResetPageFaultReadAhead( Thread, SavedState ); + + if (AbnormalTermination() && (*Bcb != NULL)) { + CcUnpinFileData( (PBCB)*Bcb, TRUE, UNPIN ); + *Bcb = NULL; + } + } + + CcMissCounter = &CcThrowAway; + + // + // Increment the pointer as a reminder that it is read only. + // + + *(PCHAR *)Bcb += 1; + + DebugTrace(-1, me, "CcMapData -> TRUE\n", 0 ); + + return TRUE; +} + + +BOOLEAN +CcPinMappedData ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + IN OUT PVOID *Bcb + ) + +/*++ + +Routine Description: + + This routine attempts to pin data that was previously only mapped. + If the routine determines that in fact it was necessary to actually + pin the data when CcMapData was called, then this routine does not + have to do anything. + + If the caller does not want to block on this call, then + Wait should be supplied as FALSE. If Wait was supplied as FALSE and + it is currently impossible to supply the requested data without + blocking, then this routine will return FALSE. However, if the + data is immediately accessible in the cache and no blocking is + required, this routine returns TRUE with a pointer to the data. + + If the data is not returned in the first call, the caller + may request the data later with Wait = TRUE. It is not required + that the caller request the data later. + + If the caller subsequently modifies the data, it should call + CcSetDirtyPinnedData. + + In any case, the caller MUST subsequently call CcUnpinData. + Naturally if CcPinRead or CcPreparePinWrite were called multiple + times for the same data, CcUnpinData must be called the same number + of times. + + Note there are no performance counters in this routine, as the misses + will almost always occur on the map above, and there will seldom be a + miss on this conversion. + +Arguments: + + FileObject - Pointer to the file object for a file which was + opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for + which CcInitializeCacheMap was called by the file system. + + FileOffset - Byte offset in file for desired data. + + Length - Length of desired data in bytes. + + Wait - FALSE if caller may not block, TRUE otherwise (see description + above) + + Bcb - On the first call this returns a pointer to a Bcb + parameter which must be supplied as input on all subsequent + calls, for this buffer + +Return Value: + + FALSE - if Wait was supplied as FALSE and the data was not delivered + + TRUE - if the data is being delivered + +--*/ + +{ + PVOID Buffer; + LARGE_INTEGER BeyondLastByte; + PSHARED_CACHE_MAP SharedCacheMap; + LARGE_INTEGER LocalFileOffset = *FileOffset; + POBCB MyBcb = NULL; + PBCB *CurrentBcbPtr = (PBCB *)&MyBcb; + BOOLEAN Result = FALSE; + + DebugTrace(+1, me, "CcPinMappedData\n", 0 ); + + // + // If the Bcb is no longer ReadOnly, then just return. + // + + if ((*(PULONG)Bcb & 1) == 0) { + return TRUE; + } + + // + // Remove the Read Only flag + // + + *(PCHAR *)Bcb -= 1; + + // + // Get pointer to SharedCacheMap. + // + + SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer + + sizeof(PVOID)); + + // + // We only count the calls to this routine, since they are almost guaranteed + // to be hits. + // + + CcPinMappedDataCount += 1; + + // + // Guarantee we will put the flag back if required. + // + + try { + + if (((PBCB)*Bcb)->NodeTypeCode != CACHE_NTC_BCB) { + + // + // Form loop to handle occassional overlapped Bcb case. + // + + do { + + // + // If we have already been through the loop, then adjust + // our file offset and length from the last time. + // + + if (MyBcb != NULL) { + + // + // If this is the second time through the loop, then it is time + // to handle the overlap case and allocate an OBCB. + // + + if (CurrentBcbPtr == (PBCB *)&MyBcb) { + + MyBcb = CcAllocateObcb( FileOffset, Length, (PBCB)MyBcb ); + + // + // Set CurrentBcbPtr to point at the first entry in + // the vector (which is already filled in), before + // advancing it below. + // + + CurrentBcbPtr = &MyBcb->Bcbs[0]; + } + + Length -= (ULONG)(BeyondLastByte.QuadPart - LocalFileOffset.QuadPart); + LocalFileOffset.QuadPart = BeyondLastByte.QuadPart; + CurrentBcbPtr += 1; + } + + // + // Call local routine to Map or Access the file data. If we cannot map + // the data because of a Wait condition, return FALSE. + // + + if (!CcPinFileData( FileObject, + &LocalFileOffset, + Length, + (BOOLEAN)!FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED), + FALSE, + Wait, + CurrentBcbPtr, + &Buffer, + &BeyondLastByte )) { + + try_return( Result = FALSE ); + } + + // + // Continue looping if we did not get everything. + // + + } while((BeyondLastByte.QuadPart - LocalFileOffset.QuadPart) < Length); + + // + // Free the Vacb before going on. + // + + CcFreeVirtualAddress( (PVACB)*Bcb ); + + *Bcb = MyBcb; + + // + // Debug routines used to insert and remove Bcbs from the global list + // + +#if LIST_DBG + { + KIRQL OldIrql; + PBCB BcbTemp = (PBCB)*Bcb; + + ExAcquireSpinLock( &CcBcbSpinLock, &OldIrql ); + + if (BcbTemp->CcBcbLinks.Flink == NULL) { + + InsertTailList( &CcBcbList, &BcbTemp->CcBcbLinks ); + CcBcbCount += 1; + ExReleaseSpinLock( &CcBcbSpinLock, OldIrql ); + SetCallersAddress( BcbTemp ); + + } else { + ExReleaseSpinLock( &CcBcbSpinLock, OldIrql ); + } + + } +#endif + } + + // + // If he really has a Bcb, all we have to do is acquire it shared since he is + // no longer ReadOnly. + // + + else { + + if (!ExAcquireSharedStarveExclusive( &((PBCB)*Bcb)->Resource, Wait )) { + + try_return( Result = FALSE ); + } + } + + Result = TRUE; + + try_exit: NOTHING; + } + finally { + + if (!Result) { + + // + // Put the Read Only flag back + // + + *(PCHAR *)Bcb += 1; + + // + // We may have gotten partway through + // + + if (MyBcb != NULL) { + CcUnpinData( MyBcb ); + } + } + + DebugTrace(-1, me, "CcPinMappedData -> %02lx\n", Result ); + } + return Result; +} + + +BOOLEAN +CcPinRead ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Wait, + OUT PVOID *Bcb, + OUT PVOID *Buffer + ) + +/*++ + +Routine Description: + + This routine attempts to pin the specified file data in the cache. + A pointer is returned to the desired data in the cache. This routine + is intended for File System support and is not intended to be called + from Dpc level. + + If the caller does not want to block on this call, then + Wait should be supplied as FALSE. If Wait was supplied as FALSE and + it is currently impossible to supply the requested data without + blocking, then this routine will return FALSE. However, if the + data is immediately accessible in the cache and no blocking is + required, this routine returns TRUE with a pointer to the data. + + If the data is not returned in the first call, the caller + may request the data later with Wait = TRUE. It is not required + that the caller request the data later. + + If the caller subsequently modifies the data, it should call + CcSetDirtyPinnedData. + + In any case, the caller MUST subsequently call CcUnpinData. + Naturally if CcPinRead or CcPreparePinWrite were called multiple + times for the same data, CcUnpinData must be called the same number + of times. + + The returned Buffer pointer is valid until the data is unpinned, at + which point it is invalid to use the pointer further. + +Arguments: + + FileObject - Pointer to the file object for a file which was + opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for + which CcInitializeCacheMap was called by the file system. + + FileOffset - Byte offset in file for desired data. + + Length - Length of desired data in bytes. + + Wait - Supplies TRUE if it is ok to block the caller's thread + Supplies 3 if it is ok to block the caller's thread and the Bcb should + be exclusive + Supplies FALSE if it is not ok to block the caller's thread + + Bcb - On the first call this returns a pointer to a Bcb + parameter which must be supplied as input on all subsequent + calls, for this buffer + + Buffer - Returns pointer to desired data, valid until the buffer is + unpinned or freed. + +Return Value: + + FALSE - if Wait was supplied as FALSE and the data was not delivered + + TRUE - if the data is being delivered + +--*/ + +{ + PSHARED_CACHE_MAP SharedCacheMap; + PVOID LocalBuffer; + LARGE_INTEGER BeyondLastByte; + LARGE_INTEGER LocalFileOffset = *FileOffset; + POBCB MyBcb = NULL; + PBCB *CurrentBcbPtr = (PBCB *)&MyBcb; + BOOLEAN Result = FALSE; + + DebugTrace(+1, me, "CcPinRead\n", 0 ); + + // + // Increment performance counters + // + + if (Wait) { + + CcPinReadWait += 1; + + // + // Initialize the indirect pointer to our miss counter. + // + + CcMissCounter = &CcPinReadWaitMiss; + + } else { + CcPinReadNoWait += 1; + } + + // + // Get pointer to SharedCacheMap. + // + + SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer + + sizeof(PVOID)); + + try { + + // + // Form loop to handle occassional overlapped Bcb case. + // + + do { + + // + // If we have already been through the loop, then adjust + // our file offset and length from the last time. + // + + if (MyBcb != NULL) { + + // + // If this is the second time through the loop, then it is time + // to handle the overlap case and allocate an OBCB. + // + + if (CurrentBcbPtr == (PBCB *)&MyBcb) { + + MyBcb = CcAllocateObcb( FileOffset, Length, (PBCB)MyBcb ); + + // + // Set CurrentBcbPtr to point at the first entry in + // the vector (which is already filled in), before + // advancing it below. + // + + CurrentBcbPtr = &MyBcb->Bcbs[0]; + + // + // Also on second time through, return starting Buffer + // + + *Buffer = LocalBuffer; + } + + Length -= (ULONG)(BeyondLastByte.QuadPart - LocalFileOffset.QuadPart); + LocalFileOffset.QuadPart = BeyondLastByte.QuadPart; + CurrentBcbPtr += 1; + } + + // + // Call local routine to Map or Access the file data. If we cannot map + // the data because of a Wait condition, return FALSE. + // + + if (!CcPinFileData( FileObject, + &LocalFileOffset, + Length, + (BOOLEAN)!FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED), + FALSE, + Wait, + CurrentBcbPtr, + &LocalBuffer, + &BeyondLastByte )) { + + CcPinReadNoWaitMiss += 1; + + try_return( Result = FALSE ); + } + + // + // Continue looping if we did not get everything. + // + + } while((BeyondLastByte.QuadPart - LocalFileOffset.QuadPart) < Length); + + *Bcb = MyBcb; + + // + // Debug routines used to insert and remove Bcbs from the global list + // + +#if LIST_DBG + + { + KIRQL OldIrql; + PBCB BcbTemp = (PBCB)*Bcb; + + ExAcquireSpinLock( &CcBcbSpinLock, &OldIrql ); + + if (BcbTemp->CcBcbLinks.Flink == NULL) { + + InsertTailList( &CcBcbList, &BcbTemp->CcBcbLinks ); + CcBcbCount += 1; + ExReleaseSpinLock( &CcBcbSpinLock, OldIrql ); + SetCallersAddress( BcbTemp ); + + } else { + ExReleaseSpinLock( &CcBcbSpinLock, OldIrql ); + } + + } + +#endif + + // + // In the normal (nonoverlapping) case we return the + // correct buffer address here. + // + + if (CurrentBcbPtr == (PBCB *)&MyBcb) { + *Buffer = LocalBuffer; + } + + Result = TRUE; + + try_exit: NOTHING; + } + finally { + + CcMissCounter = &CcThrowAway; + + if (!Result) { + + // + // We may have gotten partway through + // + + if (MyBcb != NULL) { + CcUnpinData( MyBcb ); + } + } + + DebugTrace(-1, me, "CcPinRead -> %02lx\n", Result ); + } + + return Result; +} + + +BOOLEAN +CcPreparePinWrite ( + IN PFILE_OBJECT FileObject, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN BOOLEAN Zero, + IN BOOLEAN Wait, + OUT PVOID *Bcb, + OUT PVOID *Buffer + ) + +/*++ + +Routine Description: + + This routine attempts to lock the specified file data in the cache + and return a pointer to it along with the correct + I/O status. Pages to be completely overwritten may be satisfied + with emtpy pages. + + If not all of the pages can be prepared, and Wait was supplied as + FALSE, then this routine will return FALSE, and its outputs will + be meaningless. The caller may request the data later with + Wait = TRUE. However, it is not required that the caller request + the data later. + + If Wait is supplied as TRUE, and all of the pages can be prepared + without blocking, this call will return TRUE immediately. Otherwise, + this call will block until all of the pages can be prepared, and + then return TRUE. + + When this call returns with TRUE, the caller may immediately begin + to transfer data into the buffers via the Buffer pointer. The + buffer will already be marked dirty. + + The caller MUST subsequently call CcUnpinData. + Naturally if CcPinRead or CcPreparePinWrite were called multiple + times for the same data, CcUnpinData must be called the same number + of times. + + The returned Buffer pointer is valid until the data is unpinned, at + which point it is invalid to use the pointer further. + +Arguments: + + FileObject - Pointer to the file object for a file which was + opened with NO_INTERMEDIATE_BUFFERING clear, i.e., for + which CcInitializeCacheMap was called by the file system. + + FileOffset - Byte offset in file for desired data. + + Length - Length of desired data in bytes. + + Zero - If supplied as TRUE, the buffer will be zeroed on return. + + Wait - FALSE if caller may not block, TRUE otherwise (see description + above) + + Bcb - This returns a pointer to a Bcb parameter which must be + supplied as input to CcPinWriteComplete. + + Buffer - Returns pointer to desired data, valid until the buffer is + unpinned or freed. + +Return Value: + + FALSE - if Wait was supplied as FALSE and the pages were not delivered + + TRUE - if the pages are being delivered + +--*/ + +{ + PSHARED_CACHE_MAP SharedCacheMap; + PVOID LocalBuffer; + LARGE_INTEGER BeyondLastByte; + LARGE_INTEGER LocalFileOffset = *FileOffset; + POBCB MyBcb = NULL; + PBCB *CurrentBcbPtr = (PBCB *)&MyBcb; + ULONG OriginalLength = Length; + BOOLEAN Result = FALSE; + + DebugTrace(+1, me, "CcPreparePinWrite\n", 0 ); + + // + // Get pointer to SharedCacheMap. + // + + SharedCacheMap = *(PSHARED_CACHE_MAP *)((PCHAR)FileObject->SectionObjectPointer + + sizeof(PVOID)); + + try { + + // + // Form loop to handle occassional overlapped Bcb case. + // + + do { + + // + // If we have already been through the loop, then adjust + // our file offset and length from the last time. + // + + if (MyBcb != NULL) { + + // + // If this is the second time through the loop, then it is time + // to handle the overlap case and allocate an OBCB. + // + + if (CurrentBcbPtr == (PBCB *)&MyBcb) { + + MyBcb = CcAllocateObcb( FileOffset, Length, (PBCB)MyBcb ); + + // + // Set CurrentBcbPtr to point at the first entry in + // the vector (which is already filled in), before + // advancing it below. + // + + CurrentBcbPtr = &MyBcb->Bcbs[0]; + + // + // Also on second time through, return starting Buffer + // + + *Buffer = LocalBuffer; + } + + Length -= (ULONG)(BeyondLastByte.QuadPart - LocalFileOffset.QuadPart); + LocalFileOffset.QuadPart = BeyondLastByte.QuadPart; + CurrentBcbPtr += 1; + } + + // + // Call local routine to Map or Access the file data. If we cannot map + // the data because of a Wait condition, return FALSE. + // + + if (!CcPinFileData( FileObject, + &LocalFileOffset, + Length, + FALSE, + TRUE, + Wait, + CurrentBcbPtr, + &LocalBuffer, + &BeyondLastByte )) { + + try_return( Result = FALSE ); + } + + // + // Continue looping if we did not get everything. + // + + } while((BeyondLastByte.QuadPart - LocalFileOffset.QuadPart) < Length); + + *Bcb = MyBcb; + + // + // Debug routines used to insert and remove Bcbs from the global list + // + +#if LIST_DBG + + { + KIRQL OldIrql; + PBCB BcbTemp = (PBCB)*Bcb; + + ExAcquireSpinLock( &CcBcbSpinLock, &OldIrql ); + + if (BcbTemp->CcBcbLinks.Flink == NULL) { + + InsertTailList( &CcBcbList, &BcbTemp->CcBcbLinks ); + CcBcbCount += 1; + ExReleaseSpinLock( &CcBcbSpinLock, OldIrql ); + SetCallersAddress( BcbTemp ); + + } else { + ExReleaseSpinLock( &CcBcbSpinLock, OldIrql ); + } + + } + +#endif + + // + // In the normal (nonoverlapping) case we return the + // correct buffer address here. + // + + if (CurrentBcbPtr == (PBCB *)&MyBcb) { + *Buffer = LocalBuffer; + } + + if (Zero) { + RtlZeroMemory( *Buffer, OriginalLength ); + } + + CcSetDirtyPinnedData( MyBcb, NULL ); + + Result = TRUE; + + try_exit: NOTHING; + } + finally { + + CcMissCounter = &CcThrowAway; + + if (!Result) { + + // + // We may have gotten partway through + // + + if (MyBcb != NULL) { + CcUnpinData( MyBcb ); + } + } + + DebugTrace(-1, me, "CcPreparePinWrite -> %02lx\n", Result ); + } + + return Result; +} + + +VOID +CcUnpinData ( + IN PVOID Bcb + ) + +/*++ + +Routine Description: + + This routine must be called at IPL0, some time after calling CcPinRead + or CcPreparePinWrite. It performs any cleanup that is necessary. + +Arguments: + + Bcb - Bcb parameter returned from the last call to CcPinRead. + +Return Value: + + None. + +--*/ + +{ + DebugTrace(+1, me, "CcUnpinData:\n", 0 ); + DebugTrace( 0, me, " >Bcb = %08lx\n", Bcb ); + + // + // Test for ReadOnly and unpin accordingly. + // + + if (((ULONG)Bcb & 1) != 0) { + + // + // Remove the Read Only flag + // + + (PCHAR)Bcb -= 1; + + CcUnpinFileData( (PBCB)Bcb, TRUE, UNPIN ); + + } else { + + // + // Handle the overlapped Bcb case. + // + + if (((POBCB)Bcb)->NodeTypeCode == CACHE_NTC_OBCB) { + + PBCB *BcbPtrPtr = &((POBCB)Bcb)->Bcbs[0]; + + // + // Loop to free all Bcbs with recursive calls + // (rather than dealing with RO for this uncommon case). + // + + while (*BcbPtrPtr != NULL) { + CcUnpinData(*(BcbPtrPtr++)); + } + + // + // Then free the pool for the Obcb + // + + ExFreePool( Bcb ); + + // + // Otherwise, it is a normal Bcb + // + + } else { + CcUnpinFileData( (PBCB)Bcb, FALSE, UNPIN ); + } + } + + DebugTrace(-1, me, "CcUnPinData -> VOID\n", 0 ); +} + + +VOID +CcSetBcbOwnerPointer ( + IN PVOID Bcb, + IN PVOID OwnerPointer + ) + +/*++ + +Routine Description: + + This routine may be called to set the resource owner for the Bcb resource, + for cases where another thread will do the unpin *and* the current thread + may exit. + +Arguments: + + Bcb - Bcb parameter returned from the last call to CcPinRead. + + OwnerPointer - A valid resource owner pointer, which means a pointer to + an allocated system address, with the low-order two bits + set. The address may not be deallocated until after the + unpin call. + +Return Value: + + None. + +--*/ + +{ + ASSERT(((ULONG)Bcb & 1) == 0); + + // + // Handle the overlapped Bcb case. + // + + if (((POBCB)Bcb)->NodeTypeCode == CACHE_NTC_OBCB) { + + PBCB *BcbPtrPtr = &((POBCB)Bcb)->Bcbs[0]; + + // + // Loop to set owner for all Bcbs. + // + + while (*BcbPtrPtr != NULL) { + ExSetResourceOwnerPointer( &(*BcbPtrPtr)->Resource, OwnerPointer ); + BcbPtrPtr++; + } + + // + // Otherwise, it is a normal Bcb + // + + } else { + + // + // Handle normal case. + // + + ExSetResourceOwnerPointer( &((PBCB)Bcb)->Resource, OwnerPointer ); + } +} + + +VOID +CcUnpinDataForThread ( + IN PVOID Bcb, + IN ERESOURCE_THREAD ResourceThreadId + ) + +/*++ + +Routine Description: + + This routine must be called at IPL0, some time after calling CcPinRead + or CcPreparePinWrite. It performs any cleanup that is necessary, + releasing the Bcb resource for the given thread. + +Arguments: + + Bcb - Bcb parameter returned from the last call to CcPinRead. + +Return Value: + + None. + +--*/ + +{ + DebugTrace(+1, me, "CcUnpinDataForThread:\n", 0 ); + DebugTrace( 0, me, " >Bcb = %08lx\n", Bcb ); + DebugTrace( 0, me, " >ResoureceThreadId = %08lx\n", ResoureceThreadId ); + + // + // Test for ReadOnly and unpin accordingly. + // + + if (((ULONG)Bcb & 1) != 0) { + + // + // Remove the Read Only flag + // + + (PCHAR)Bcb -= 1; + + CcUnpinFileData( (PBCB)Bcb, TRUE, UNPIN ); + + } else { + + // + // Handle the overlapped Bcb case. + // + + if (((POBCB)Bcb)->NodeTypeCode == CACHE_NTC_OBCB) { + + PBCB *BcbPtrPtr = &((POBCB)Bcb)->Bcbs[0]; + + // + // Loop to free all Bcbs with recursive calls + // (rather than dealing with RO for this uncommon case). + // + + while (*BcbPtrPtr != NULL) { + CcUnpinDataForThread( *(BcbPtrPtr++), ResourceThreadId ); + } + + // + // Then free the pool for the Obcb + // + + ExFreePool( Bcb ); + + // + // Otherwise, it is a normal Bcb + // + + } else { + + // + // If not readonly, we can release the resource for the thread first, + // and then call CcUnpinFileData. Release resource first in case + // Bcb gets deallocated. + // + + ExReleaseResourceForThread( &((PBCB)Bcb)->Resource, ResourceThreadId ); + CcUnpinFileData( (PBCB)Bcb, TRUE, UNPIN ); + } + } + DebugTrace(-1, me, "CcUnpinDataForThread -> VOID\n", 0 ); +} + + +POBCB +CcAllocateObcb ( + IN PLARGE_INTEGER FileOffset, + IN ULONG Length, + IN PBCB FirstBcb + ) + +/*++ + +Routine Description: + + This routine is called by the various pinning routines to allocate and + initialize an overlap Bcb. + +Arguments: + + FileOffset - Starting file offset for the Obcb (An Obcb starts with a + public structure, which someone could use) + + Length - Length of the range covered by the Obcb + + FirstBcb - First Bcb already created, which only covers the start of + the desired range (low order bit may be set to indicate ReadOnly) + +Return Value: + + Pointer to the allocated Obcb + +--*/ + +{ + ULONG LengthToAllocate; + POBCB Obcb; + + // + // Allocate according to the worst case, assuming that we + // will need as many additional Bcbs as there are pages + // remaining. (One Bcb pointer is already in OBCB.) Also + // throw in one more pointer to guarantee users of the OBCB + // can always terminate on NULL. + // + + LengthToAllocate = sizeof(OBCB) + + (((Length - ((PBCB)((ULONG)FirstBcb & ~1))->ByteLength + + (2 * PAGE_SIZE) - 1) / PAGE_SIZE) * sizeof(PBCB)); + + Obcb = FsRtlAllocatePool( NonPagedPool, LengthToAllocate ); + RtlZeroMemory( Obcb, LengthToAllocate ); + Obcb->NodeTypeCode = CACHE_NTC_OBCB; + Obcb->NodeByteSize = (USHORT)LengthToAllocate; + Obcb->ByteLength = Length; + Obcb->FileOffset = *FileOffset; + Obcb->Bcbs[0] = FirstBcb; + + return Obcb; +} |