summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr/cache.c')
-rw-r--r--private/nw/rdr/cache.c698
1 files changed, 698 insertions, 0 deletions
diff --git a/private/nw/rdr/cache.c b/private/nw/rdr/cache.c
new file mode 100644
index 000000000..0729b9321
--- /dev/null
+++ b/private/nw/rdr/cache.c
@@ -0,0 +1,698 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Cache.c
+
+Abstract:
+
+ This module implements internal caching support routines. It does
+ not interact with the cache manager.
+
+Author:
+
+ Manny Weiser [MannyW] 05-Jan-1994
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// The local debug trace level
+//
+
+BOOLEAN
+SpaceForWriteBehind(
+ PNONPAGED_FCB NpFcb,
+ ULONG FileOffset,
+ ULONG BytesToWrite
+ );
+
+BOOLEAN
+OkToReadAhead(
+ PFCB Fcb,
+ IN ULONG FileOffset,
+ IN UCHAR IoType
+ );
+
+#define Dbg (DEBUG_TRACE_CACHE)
+
+//
+// Local procedure prototypes
+//
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, CacheRead )
+#pragma alloc_text( PAGE, SpaceForWriteBehind )
+#pragma alloc_text( PAGE, CacheWrite )
+#pragma alloc_text( PAGE, OkToReadAhead )
+#pragma alloc_text( PAGE, CalculateReadAheadSize )
+#pragma alloc_text( PAGE, FlushCache )
+#pragma alloc_text( PAGE, AcquireFcbAndFlushCache )
+#endif
+
+
+ULONG
+CacheRead(
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG FileOffset,
+ IN ULONG BytesToRead,
+ IN PVOID UserBuffer
+ , IN BOOLEAN WholeBufferOnly
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to satisfy a user read from cache. It returns
+ the number of bytes actually copied from cache.
+
+Arguments:
+
+ NpFcb - A pointer the the nonpaged FCB of the file being read.
+
+ FileOffset - The file offset to read.
+
+ BytesToRead - The number of bytes to read.
+
+ UserBuffer - A pointer to the users target buffer.
+
+ WholeBufferOnly - Do a cache read only if we can satisfy the entire
+ read request.
+
+Return Value:
+
+ The number of bytes copied to the user buffer.
+
+--*/
+{
+ ULONG BytesToCopy;
+
+ PAGED_CODE();
+
+ if (DisableReadCache) return 0 ;
+
+ DebugTrace(0, Dbg, "CacheRead...\n", 0 );
+ DebugTrace( 0, Dbg, "FileOffset = %d\n", FileOffset );
+ DebugTrace( 0, Dbg, "ByteCount = %d\n", BytesToRead );
+
+ NwAcquireSharedFcb( NpFcb, TRUE );
+
+ //
+ // If this is a read ahead and it contains some data that the user
+ // could be interested in, copy the interesting data.
+ //
+
+ if ( NpFcb->CacheType == ReadAhead &&
+ NpFcb->CacheDataSize != 0 &&
+ FileOffset >= NpFcb->CacheFileOffset &&
+ FileOffset <= NpFcb->CacheFileOffset + NpFcb->CacheDataSize ) {
+
+ if ( NpFcb->CacheBuffer ) {
+
+ //
+ // Make sure we have a CacheBuffer.
+ //
+
+ BytesToCopy =
+ MIN ( BytesToRead,
+ NpFcb->CacheFileOffset +
+ NpFcb->CacheDataSize - FileOffset );
+
+ if ( WholeBufferOnly && BytesToCopy != BytesToRead ) {
+ NwReleaseFcb( NpFcb );
+ return( 0 );
+ }
+
+ RtlCopyMemory(
+ UserBuffer,
+ NpFcb->CacheBuffer + ( FileOffset - NpFcb->CacheFileOffset ),
+ BytesToCopy );
+
+ DebugTrace(0, Dbg, "CacheRead -> %d\n", BytesToCopy );
+
+ } else {
+
+ ASSERT(FALSE); // we should never get here
+ DebugTrace(0, Dbg, "CacheRead -> %08lx\n", 0 );
+ BytesToCopy = 0;
+ }
+
+
+ } else {
+
+ DebugTrace(0, Dbg, "CacheRead -> %08lx\n", 0 );
+ BytesToCopy = 0;
+ }
+
+ NwReleaseFcb( NpFcb );
+ return( BytesToCopy );
+}
+
+
+BOOLEAN
+SpaceForWriteBehind(
+ PNONPAGED_FCB NpFcb,
+ ULONG FileOffset,
+ ULONG BytesToWrite
+ )
+/*++
+
+Routine Description:
+
+ This routine determines if it is ok to write behind this data to
+ this FCB.
+
+Arguments:
+
+ NpFcb - A pointer the the NONPAGED_FCB of the file being written.
+
+ FileOffset - The file offset to write.
+
+ BytesToWrite - The number of bytes to write.
+
+Return Value:
+
+ The number of bytes copied to the user buffer.
+
+--*/
+{
+ PAGED_CODE();
+
+
+ if ( NpFcb->CacheDataSize == 0 ) {
+ NpFcb->CacheFileOffset = FileOffset;
+ }
+
+ if ( NpFcb->CacheDataSize == 0 && BytesToWrite >= NpFcb->CacheSize ) {
+ return( FALSE );
+ }
+
+ if ( FileOffset - NpFcb->CacheFileOffset + BytesToWrite >
+ NpFcb->CacheSize ) {
+
+ return( FALSE );
+
+ }
+
+ return( TRUE );
+}
+
+
+BOOLEAN
+CacheWrite(
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG FileOffset,
+ IN ULONG BytesToWrite,
+ IN PVOID UserBuffer
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to satisfy a user write to cache. The write
+ succeeds if it is sequential and fits in the cache buffer.
+
+Arguments:
+
+ IrpContext - A pointer to request parameters.
+
+ NpFcb - A pointer the the NONPAGED_FCB of the file being read.
+
+ FileOffset - The file offset to write.
+
+ BytesToWrite - The number of bytes to write.
+
+ UserBuffer - A pointer to the users source buffer.
+
+Return Value:
+
+ The number of bytes copied to the user buffer.
+
+--*/
+{
+ ULONG CacheSize;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ if (DisableWriteCache) return FALSE ;
+
+ DebugTrace( +1, Dbg, "CacheWrite...\n", 0 );
+ DebugTrace( 0, Dbg, "FileOffset = %d\n", FileOffset );
+ DebugTrace( 0, Dbg, "ByteCount = %d\n", BytesToWrite );
+
+ if ( NpFcb->Fcb->ShareAccess.SharedWrite ||
+ NpFcb->Fcb->ShareAccess.SharedRead ) {
+
+ DebugTrace( 0, Dbg, "File is not open in exclusive mode\n", 0 );
+ DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ return( FALSE );
+ }
+
+ //
+ // Note, If we decide to send data to the server we must be at the front
+ // of the queue before we grab the Fcb exclusive.
+ //
+
+TryAgain:
+
+ NwAcquireExclusiveFcb( NpFcb, TRUE );
+
+ //
+ // Allocate a cache buffer if we don't already have one.
+ //
+
+ if ( NpFcb->CacheBuffer == NULL ) {
+
+ if ( IrpContext == NULL ) {
+ DebugTrace( 0, Dbg, "No cache buffer\n", 0 );
+ DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+ }
+
+ NpFcb->CacheType = WriteBehind;
+
+ if (( IrpContext->pNpScb->SendBurstModeEnabled ) ||
+ ( IrpContext->pNpScb->ReceiveBurstModeEnabled )) {
+
+ CacheSize = IrpContext->pNpScb->MaxReceiveSize;
+
+ } else {
+
+ CacheSize = IrpContext->pNpScb->BufferSize;
+
+ }
+
+ try {
+
+ NpFcb->CacheBuffer = ALLOCATE_POOL_EX( NonPagedPool, CacheSize );
+ NpFcb->CacheSize = CacheSize;
+
+ NpFcb->CacheMdl = ALLOCATE_MDL( NpFcb->CacheBuffer, CacheSize, FALSE, FALSE, NULL );
+
+ if ( NpFcb->CacheMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmBuildMdlForNonPagedPool( NpFcb->CacheMdl );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ if ( NpFcb->CacheBuffer != NULL) {
+ FREE_POOL( NpFcb->CacheBuffer );
+
+ NpFcb->CacheBuffer = NULL;
+ NpFcb->CacheSize = 0;
+
+ }
+
+ DebugTrace( 0, Dbg, "Allocate failed\n", 0 );
+ DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
+
+ NpFcb->CacheDataSize = 0;
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+ }
+
+ NpFcb->CacheFileOffset = 0;
+ NpFcb->CacheDataSize = 0;
+
+ } else if ( NpFcb->CacheType != WriteBehind ) {
+
+ DebugTrace( -1, Dbg, "CacheWrite not writebehind -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+
+ }
+
+ //
+ // If the data is non sequential and non overlapping, flush the
+ // existing cache.
+ //
+
+ if ( NpFcb->CacheDataSize != 0 &&
+ ( FileOffset < NpFcb->CacheFileOffset ||
+ FileOffset > NpFcb->CacheFileOffset + NpFcb->CacheDataSize ) ) {
+
+ //
+ // Release and then AcquireFcbAndFlush() will get us to the front
+ // of the queue before re-acquiring. This avoids potential deadlocks.
+ //
+
+ NwReleaseFcb( NpFcb );
+
+ if ( IrpContext != NULL ) {
+ DebugTrace( 0, Dbg, "Data is not sequential, flushing data\n", 0 );
+
+ status = AcquireFcbAndFlushCache( IrpContext, NpFcb );
+
+ if ( !NT_SUCCESS( status ) ) {
+ ExRaiseStatus( status );
+ }
+
+ }
+
+ DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ return( FALSE );
+
+ }
+
+ //
+ // The data is sequential, see if it fits.
+ //
+
+ if ( SpaceForWriteBehind( NpFcb, FileOffset, BytesToWrite ) ) {
+
+ try {
+
+ RtlCopyMemory(
+ NpFcb->CacheBuffer + ( FileOffset - NpFcb->CacheFileOffset ),
+ UserBuffer,
+ BytesToWrite );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user mode buffer in CacheWrite.\n", 0 );
+ DebugTrace(-1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return ( FALSE );
+ }
+
+ if ( NpFcb->CacheDataSize <
+ (FileOffset - NpFcb->CacheFileOffset + BytesToWrite) ) {
+
+ NpFcb->CacheDataSize =
+ FileOffset - NpFcb->CacheFileOffset + BytesToWrite;
+
+ }
+
+ DebugTrace(-1, Dbg, "CacheWrite -> TRUE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( TRUE );
+
+ } else if ( IrpContext != NULL ) {
+
+ //
+ // The data didn't fit in the cache. If the cache is empty
+ // then its time to return because it never will fit and we
+ // have no stale data. This can happen if this request or
+ // another being processed in parallel flush the cache and
+ // TryAgain.
+ //
+
+ if ( NpFcb->CacheDataSize == 0 ) {
+ DebugTrace(-1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+ }
+
+ //
+ // The data didn't fit in the cache, flush the cache
+ //
+
+ DebugTrace( 0, Dbg, "Cache is full, flushing data\n", 0 );
+
+ //
+ // We must be at the front of the Queue before writing.
+ //
+
+ NwReleaseFcb( NpFcb );
+
+ status = AcquireFcbAndFlushCache( IrpContext, NpFcb );
+
+ if ( !NT_SUCCESS( status ) ) {
+ ExRaiseStatus( status );
+ }
+
+ //
+ // Now see if it fits in the cache. We need to repeat all
+ // the tests again because two requests can flush the cache at the
+ // same time and the other one of them could have nearly filled it again.
+ //
+
+ goto TryAgain;
+
+ } else {
+ DebugTrace(-1, Dbg, "CacheWrite full -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+ }
+}
+
+
+BOOLEAN
+OkToReadAhead(
+ PFCB Fcb,
+ IN ULONG FileOffset,
+ IN UCHAR IoType
+ )
+/*++
+
+Routine Description:
+
+ This routine determines whether the attempted i/o is sequential (so that
+ we can use the cache).
+
+Arguments:
+
+ Fcb - A pointer the the Fcb of the file being read.
+
+ FileOffset - The file offset to read.
+
+Return Value:
+
+ TRUE - The operation is sequential.
+ FALSE - The operation is not sequential.
+
+--*/
+{
+ PAGED_CODE();
+
+ if ( Fcb->NonPagedFcb->CacheType == IoType &&
+ !Fcb->ShareAccess.SharedWrite &&
+ FileOffset == Fcb->LastReadOffset + Fcb->LastReadSize ) {
+
+ DebugTrace(0, Dbg, "Io is sequential\n", 0 );
+ return( TRUE );
+
+ } else {
+
+ DebugTrace(0, Dbg, "Io is not sequential\n", 0 );
+ return( FALSE );
+
+ }
+}
+
+
+ULONG
+CalculateReadAheadSize(
+ IN PIRP_CONTEXT IrpContext,
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG CacheReadSize,
+ IN ULONG FileOffset,
+ IN ULONG ByteCount
+ )
+/*++
+
+Routine Description:
+
+ This routine determines the amount of data that can be read ahead,
+ and sets up for the read.
+
+ Note: Fcb must be acquired exclusive before calling.
+
+Arguments:
+
+ NpFcb - A pointer the the nonpaged FCB of the file being read.
+
+ FileOffset - The file offset to read.
+
+Return Value:
+
+ The amount of data to read.
+
+--*/
+{
+ ULONG ReadSize;
+ ULONG CacheSize;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "CalculateReadAheadSize\n", 0 );
+
+ if (( IrpContext->pNpScb->SendBurstModeEnabled ) ||
+ ( IrpContext->pNpScb->ReceiveBurstModeEnabled )) {
+
+ CacheSize = IrpContext->pNpScb->MaxReceiveSize;
+
+ } else {
+
+ CacheSize = IrpContext->pNpScb->BufferSize;
+
+ }
+
+ if ( OkToReadAhead( NpFcb->Fcb, FileOffset - CacheReadSize, ReadAhead ) &&
+ ByteCount < CacheSize ) {
+
+ ReadSize = CacheSize;
+
+ } else {
+
+ //
+ // Do not read ahead.
+ //
+
+ DebugTrace( 0, Dbg, "No read ahead\n", 0 );
+ DebugTrace(-1, Dbg, "CalculateReadAheadSize -> %d\n", ByteCount );
+ return ( ByteCount );
+
+ }
+
+ //
+ // Allocate pool for the segment of the read
+ //
+
+ if ( NpFcb->CacheBuffer == NULL ) {
+
+ try {
+
+ NpFcb->CacheBuffer = ALLOCATE_POOL_EX( NonPagedPool, ReadSize );
+ NpFcb->CacheSize = ReadSize;
+
+ NpFcb->CacheMdl = ALLOCATE_MDL( NpFcb->CacheBuffer, ReadSize, FALSE, FALSE, NULL );
+ if ( NpFcb->CacheMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmBuildMdlForNonPagedPool( NpFcb->CacheMdl );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ if ( NpFcb->CacheBuffer != NULL) {
+ FREE_POOL( NpFcb->CacheBuffer );
+
+ NpFcb->CacheBuffer = NULL;
+ }
+
+ NpFcb->CacheSize = 0;
+ NpFcb->CacheDataSize = 0;
+
+ DebugTrace( 0, Dbg, "Failed to allocated buffer\n", 0 );
+ DebugTrace(-1, Dbg, "CalculateReadAheadSize -> %d\n", ByteCount );
+ return( ByteCount );
+ }
+
+ } else {
+ ReadSize = MIN ( NpFcb->CacheSize, ReadSize );
+ }
+
+ DebugTrace(-1, Dbg, "CalculateReadAheadSize -> %d\n", ReadSize );
+ return( ReadSize );
+}
+
+NTSTATUS
+FlushCache(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_FCB NpFcb
+ )
+/*++
+
+Routine Description:
+
+ This routine flushes the cache buffer for the NpFcb. The caller must
+ have acquired the FCB exclusive prior to making this call!
+
+Arguments:
+
+ IrpContext - A pointer to request parameters.
+
+ NpFcb - A pointer the the nonpaged FCB of the file to flush.
+
+Return Value:
+
+ The amount of data to read.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ if ( NpFcb->CacheDataSize != 0 && NpFcb->CacheType == WriteBehind ) {
+
+ LARGE_INTEGER ByteOffset;
+
+ ByteOffset.QuadPart = NpFcb->CacheFileOffset;
+
+ status = DoWrite(
+ IrpContext,
+ ByteOffset,
+ NpFcb->CacheDataSize,
+ NpFcb->CacheBuffer,
+ NpFcb->CacheMdl );
+
+ //
+ // DoWrite leaves us at the head of the queue. The caller
+ // is responsible for dequeueing the irp context appropriately.
+ //
+
+ if ( NT_SUCCESS( status ) ) {
+ NpFcb->CacheDataSize = 0;
+ }
+ }
+
+ return( status );
+}
+
+NTSTATUS
+AcquireFcbAndFlushCache(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_FCB NpFcb
+ )
+/*++
+
+Routine Description:
+
+ This routine acquires the FCB exclusive and flushes the cache
+ buffer for the acquired NpFcb.
+
+Arguments:
+
+ IrpContext - A pointer to request parameters.
+
+ NpFcb - A pointer the the nonpaged FCB of the file to flush.
+
+Return Value:
+
+ The amount of data to read.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ NwAcquireExclusiveFcb( NpFcb, TRUE );
+
+ status = FlushCache( IrpContext, NpFcb );
+
+ //
+ // Release the FCB and remove ourselves from the queue.
+ // Frequently the caller will want to grab a resource so
+ // we need to be off the queue then.
+ //
+
+ NwReleaseFcb( NpFcb );
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ return( status );
+}