/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    cc.h

Abstract:

    This module is a header file for the Memory Management based cache
    management routines for the common Cache subsystem.

Author:

    Tom Miller      [TomM]      4-May-1990

Revision History:

--*/

#ifndef _CCh_
#define _CCh_

#pragma warning(disable:4214)   // bit field types other than int
#pragma warning(disable:4201)   // nameless struct/union
#pragma warning(disable:4127)   // condition expression is constant
#pragma warning(disable:4115)   // named type definition in parentheses

#include <ntos.h>
#include <NtIoLogc.h>

#ifdef MEMPRINT
#include <memprint.h>
#endif

//
// Define macros to acquire and release cache manager locks.
//

#define CcAcquireMasterLock( OldIrql ) \
    *( OldIrql ) = KeAcquireQueuedSpinLock( LockQueueMasterLock )

#define CcReleaseMasterLock( OldIrql ) \
    KeReleaseQueuedSpinLock( LockQueueMasterLock, OldIrql )

#define CcAcquireMasterLockAtDpcLevel() \
    KeAcquireQueuedSpinLockAtDpcLevel( &KeGetCurrentPrcb()->LockQueue[LockQueueMasterLock] )

#define CcReleaseMasterLockFromDpcLevel() \
    KeReleaseQueuedSpinLockFromDpcLevel( &KeGetCurrentPrcb()->LockQueue[LockQueueMasterLock] )

#define CcAcquireVacbLock( OldIrql ) \
    *( OldIrql ) = KeAcquireQueuedSpinLock( LockQueueVacbLock )

#define CcReleaseVacbLock( OldIrql ) \
    KeReleaseQueuedSpinLock( LockQueueVacbLock, OldIrql )

#define CcAcquireVacbLockAtDpcLevel() \
    KeAcquireQueuedSpinLockAtDpcLevel( &KeGetCurrentPrcb()->LockQueue[LockQueueVacbLock] )

#define CcReleaseVacbLockFromDpcLevel() \
    KeReleaseQueuedSpinLockFromDpcLevel( &KeGetCurrentPrcb()->LockQueue[LockQueueVacbLock] )

#define CcAcquireWorkQueueLock( OldIrql ) \
    *( OldIrql ) = KeAcquireQueuedSpinLock( LockQueueWorkQueueLock )

#define CcReleaseWorkQueueLock( OldIrql ) \
    KeReleaseQueuedSpinLock( LockQueueWorkQueueLock, OldIrql )

#define CcAcquireWorkQueueLockAtDpcLevel() \
    KeAcquireQueuedSpinLockAtDpcLevel( &KeGetCurrentPrcb()->LockQueue[LockQueueWorkQueueLock] )

#define CcReleaseWorkQueueLockFromDpcLevel() \
    KeReleaseQueuedSpinLockFromDpcLevel( &KeGetCurrentPrcb()->LockQueue[LockQueueWorkQueueLock] )

//
//  This turns on the Bcb list debugging in a debug system.  Set value
//  to 0 to turn off.
//
//  ****    Note it must currently be turned off because the routines in
//          pinsup.c that manipulate this list need to be changed to do the
//          right thing for Obcbs.  Right now they get messed up by inserting Obcbs
//          (which may not be large enough among other things) into the global
//          list.  Ideally each place gets some code to insert the underlying
//          Bcbs into the list if they are not already there.
//

#if DBG
#define LIST_DBG 0
#endif

#include <FsRtl.h>

//
//  Peek at number of available pages.
//

extern PFN_COUNT MmAvailablePages;

//
//  Define our node type codes.
//

#define CACHE_NTC_SHARED_CACHE_MAP       (0x2FF)
#define CACHE_NTC_PRIVATE_CACHE_MAP      (0x2FE)
#define CACHE_NTC_BCB                    (0x2FD)
#define CACHE_NTC_DEFERRED_WRITE         (0x2FC)
#define CACHE_NTC_MBCB                   (0x2FB)
#define CACHE_NTC_OBCB                   (0x2FA)
#define CACHE_NTC_MBCB_GRANDE            (0x2F9)

//
//  The following definitions are used to generate meaningful blue bugcheck
//  screens.  On a bugcheck the file system can output 4 ulongs of useful
//  information.  The first ulong will have encoded in it a source file id
//  (in the high word) and the line number of the bugcheck (in the low word).
//  The other values can be whatever the caller of the bugcheck routine deems
//  necessary.
//
//  Each individual file that calls bugcheck needs to have defined at the
//  start of the file a constant called BugCheckFileId with one of the
//  CACHE_BUG_CHECK_ values defined below and then use CcBugCheck to bugcheck
//  the system.
//

#define CACHE_BUG_CHECK_CACHEDAT           (0x00010000)
#define CACHE_BUG_CHECK_CACHESUB           (0x00020000)
#define CACHE_BUG_CHECK_COPYSUP            (0x00030000)
#define CACHE_BUG_CHECK_FSSUP              (0x00040000)
#define CACHE_BUG_CHECK_LAZYRITE           (0x00050000)
#define CACHE_BUG_CHECK_LOGSUP             (0x00060000)
#define CACHE_BUG_CHECK_MDLSUP             (0x00070000)
#define CACHE_BUG_CHECK_PINSUP             (0x00080000)
#define CACHE_BUG_CHECK_VACBSUP            (0x00090000)

#define CcBugCheck(A,B,C) { KeBugCheckEx(CACHE_MANAGER, BugCheckFileId | __LINE__, A, B, C ); }

//
//  Define maximum View Size (These constants are currently so chosen so
//  as to be exactly a page worth of PTEs.
//

#define DEFAULT_CREATE_MODULO            ((ULONG)(0x00100000))
#define DEFAULT_EXTEND_MODULO            ((ULONG)(0x00100000))

//
//  For non FO_RANDOM_ACCESS files, define how far we go before umapping
//  views.
//

#define SEQUENTIAL_MAP_LIMIT        ((ULONG)(0x00080000))

//
//  Define some constants to drive read ahead and write behind
//

//
//  Set max read ahead.  Even though some drivers, such as AT, break up transfers >= 128kb,
//  we need to permit enough readahead to satisfy plausible cached read operation while
//  preventing denial of service attacks.
//
//  This value used to be set to 64k.  When doing cached reads in larger units (128k), we
//  would never be bringing in enough data to keep the user from blocking. 8mb is
//  arbitrarily chosen to be greater than plausible RAID bandwidth and user operation size
//  by a factor of 3-4.
//

#define MAX_READ_AHEAD                   (8 * 1024 * 1024)

//
//  Set maximum write behind / lazy write (most drivers break up transfers >= 64kb)
//

#define MAX_WRITE_BEHIND                 (MM_MAXIMUM_DISK_IO_SIZE)

//
//  Set a throttle for charging a given write against the total number of dirty
//  pages in the system, for the purpose of seeing when we should invoke write
//  throttling.
//
//  This must be the same as the throttle used for seeing when we must flush
//  temporary files in the lazy writer.  On the back of the envelope, here
//  is why:
//
//      RDP = Regular File Dirty Pages
//      TDP = Temporary File Dirty Pages
//      CWT = Charged Write Throttle
//          -> the maximum we will charge a user with when we see if
//              he should be throttled
//      TWT = Temporary Write Throttle
//          -> if we can't write this many pages, we must write temp data
//      DPT = Dirty Page Threshold
//          -> the limit when write throttling kicks in
//
//      PTD = Pages To Dirty
//      CDP = Charged Dirty Pages
//
//      Now, CDP = Min( PTD, CWT).
//
//      Excluding other effects, we throttle when:
//          #0  (RDP + TDP) + CPD >= DPT
//
//      To write temporary data, we must cause:
//          #1  (RDP + TDP) + TWT >= DPT
//
//      To release the throttle, we must eventually cause:
//          #2  (RDP + TDP) + CDP < DPT
//
//      Now, imagine TDP >> RDP (perhaps RDP == 0) and CDP == CWT for a particular
//      throttled write.
//
//      If CWT > TWT, as we drive RDP to zero (we never defer writing regular
//      data except for hotspots or other very temporary conditions), it is clear
//      that we may never trigger the writing of temporary data (#1) but also
//      never release the throttle (#2).  Simply, we would be willing to charge
//      for more dirty pages than we would be willing to guarantee are available
//      to dirty.  Hence, potential deadlock.
//
//      CWT < TWT I leave aside for the moment.  This would mean we try not to
//      allow temporary data to accumulate to the point that writes throttle as
//      a result.  Perhaps this would even be better than CWT == TWT.
//
//  It is legitimate to ask if throttling temporary data writes should be relaxed
//  if we see a large amount of dirty temp data accumulate (and it would be very
//  easy to keep track of this).  I don't claim to know the best answer to this,
//  but for now the attempt to avoid temporary data writes at all costs still
//  fits the reasonable operation mix, and we will only penalize the outside
//  oddcase with a little more throttle/release.
//

#define WRITE_CHARGE_THRESHOLD          (64 * PAGE_SIZE)

//
//  Define constants to control zeroing of file data: one constant to control
//  how much data we will actually zero ahead in the cache, and another to
//  control what the maximum transfer size is that we will use to write zeros.
//

#define MAX_ZERO_TRANSFER               (PAGE_SIZE * 128)
#define MIN_ZERO_TRANSFER               (0x10000)
#define MAX_ZEROS_IN_CACHE              (0x10000)

//
//  Definitions for multi-level Vacb structure.  The primary definition is the
//  VACB_LEVEL_SHIFT.  In a multi-level Vacb structure, level in the tree of
//  pointers has 2 ** VACB_LEVEL_SHIFT pointers.
//
//  For test, this value may be set as low as 4 (no lower), a value of 10 corresponds
//  to a convenient block size of 4KB.  (If set to 2, CcExtendVacbArray will try to
//  "push" the Vacb array allocated within the SharedCacheMap, and later someone will
//  try to deallocate the middle of the SharedCacheMap.  At 3, the MBCB_BITMAP_BLOCK_SIZE
//  is larger than MBCB_BITMAP_BLOCK_SIZE)
//
//  There is a bit of a trick as we make the jump to the multilevel structure in that
//  we need a real fixed reference count.
//

#define VACB_LEVEL_SHIFT                  (7)

//
//  This is how many bytes of pointers are at each level.  This is the size for both
//  the Vacb array and (optional) Bcb listheads.  It does not include the reference
//  block.
//

#define VACB_LEVEL_BLOCK_SIZE             ((1 << VACB_LEVEL_SHIFT) * sizeof(PVOID))

//
//  This is the last index for a level.
//

#define VACB_LAST_INDEX_FOR_LEVEL         ((1 << VACB_LEVEL_SHIFT) - 1)

//
//  This is the size of file which can be handled in a single level.
//

#define VACB_SIZE_OF_FIRST_LEVEL         (1 << (VACB_OFFSET_SHIFT + VACB_LEVEL_SHIFT))

//
//  This is the maximum number of levels it takes to support 63-bits.  It is
//  used for routines that must remember a path.
//

#define VACB_NUMBER_OF_LEVELS            (((63 - VACB_OFFSET_SHIFT)/VACB_LEVEL_SHIFT) + 1)

//
//  Define the reference structure for multilevel Vacb trees.
//

typedef struct _VACB_LEVEL_REFERENCE {

    LONG Reference;
    LONG SpecialReference;

} VACB_LEVEL_REFERENCE, *PVACB_LEVEL_REFERENCE;

//
//  Define the size of a bitmap allocated for a bitmap range, in bytes.
//

#define MBCB_BITMAP_BLOCK_SIZE           (VACB_LEVEL_BLOCK_SIZE)

//
//  Define how many bytes of a file are covered by an Mbcb bitmap range,
//  at a bit for each page.
//

#define MBCB_BITMAP_RANGE                (MBCB_BITMAP_BLOCK_SIZE * 8 * PAGE_SIZE)

//
//  Define the initial size of the Mbcb bitmap that is self-contained in the Mbcb.
//

#define MBCB_BITMAP_INITIAL_SIZE         (2 * sizeof(BITMAP_RANGE))

//
//  Define constants controlling when the Bcb list is broken into a
//  pendaflex-style array of listheads, and how the correct listhead
//  is found.  Begin when file size exceeds 2MB, and cover 512KB per
//  listhead.  At 512KB per listhead, the BcbListArray is the same
//  size as the Vacb array, i.e., it doubles the size.
//
//  The code handling these Bcb lists in the Vacb package contains
//  assumptions that the size is the same as that of the Vacb pointers.
//  Future work could undo this, but until then the size and shift
//  below cannot change.  There really isn't a good reason to want to
//  anyway.
//
//  Note that by definition a flat vacb array cannot fail to find an
//  exact match when searching for the listhead - this is only a
//  complication of the sparse structure.
//


#define BEGIN_BCB_LIST_ARRAY             (0x200000)
#define SIZE_PER_BCB_LIST                (VACB_MAPPING_GRANULARITY * 2)
#define BCB_LIST_SHIFT                   (VACB_OFFSET_SHIFT + 1)

#define GetBcbListHead(SCM,OFF,FAILSUCC) (                                                         \
  (((SCM)->SectionSize.QuadPart > BEGIN_BCB_LIST_ARRAY) &&                                         \
   FlagOn((SCM)->Flags, MODIFIED_WRITE_DISABLED)) ?                                                \
   (((SCM)->SectionSize.QuadPart > VACB_SIZE_OF_FIRST_LEVEL) ?                                     \
    CcGetBcbListHeadLargeOffset((SCM),(OFF),(FAILSUCC)) :                                          \
    (((OFF) >= (SCM)->SectionSize.QuadPart) ? &(SCM)->BcbList :                                    \
     ((PLIST_ENTRY)((SCM)->Vacbs) + (((SCM)->SectionSize.QuadPart + (OFF)) >> BCB_LIST_SHIFT)))) : \
   &(SCM)->BcbList                                                                                 \
)

//
//  Macros to lock/unlock a Vacb level as Bcbs are inserted/deleted
//

#define CcLockVacbLevel(SCM,OFF) {                                                               \
    if (((SCM)->SectionSize.QuadPart > VACB_SIZE_OF_FIRST_LEVEL) &&                              \
        FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED)) {                                \
    CcAdjustVacbLevelLockCount((SCM),(OFF), +1);}                                                \
}

#define CcUnlockVacbLevel(SCM,OFF) {                                                             \
    if (((SCM)->SectionSize.QuadPart > VACB_SIZE_OF_FIRST_LEVEL) &&                              \
        FlagOn(SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED)) {                                \
    CcAdjustVacbLevelLockCount((SCM),(OFF), -1);}                                                \
}

//
//  NOISE_BITS defines how many bits are masked off when testing for
//  sequential reads.  This allows the reader to skip up to 7 bytes
//  for alignment purposes, and we still consider the next read to be
//  sequential.  Starting and ending addresses are masked by this pattern
//  before comparison.
//

#define NOISE_BITS                       (0x7)

//
//  Define some constants to drive the Lazy Writer
//

#define LAZY_WRITER_IDLE_DELAY           ((LONG)(10000000))
#define LAZY_WRITER_COLLISION_DELAY      ((LONG)(1000000))

//
//  The following target should best be a power of 2
//

#define LAZY_WRITER_MAX_AGE_TARGET       ((ULONG)(8))

//
//  Requeue information hint for the lazy writer.
//

#define CC_REQUEUE                       35422

//
//  The global Cache Manager debug level variable, its values are:
//
//      0x00000000      Always gets printed (used when about to bug check)
//
//      0x00000001      FsSup
//      0x00000002      CacheSub
//      0x00000004      CopySup
//      0x00000008      PinSup
//
//      0x00000010      MdlSup
//      0x00000020      LazyRite
//      0x00000040
//      0x00000080
//
//      0x00000100      Trace all Mm calls
//

#define mm (0x100)

//
//  Miscellaneous support macros.
//
//      ULONG
//      FlagOn (
//          IN ULONG Flags,
//          IN ULONG SingleFlag
//          );
//
//      BOOLEAN
//      BooleanFlagOn (
//          IN ULONG Flags,
//          IN ULONG SingleFlag
//          );
//
//      VOID
//      SetFlag (
//          IN ULONG Flags,
//          IN ULONG SingleFlag
//          );
//
//      VOID
//      ClearFlag (
//          IN ULONG Flags,
//          IN ULONG SingleFlag
//          );
//
//      ULONG
//      QuadAlign (
//          IN ULONG Pointer
//          );
//

#define FlagOn(F,SF) ( \
    (((F) & (SF)))     \
)

#define BooleanFlagOn(F,SF) (    \
    (BOOLEAN)(((F) & (SF)) != 0) \
)

#define SetFlag(F,SF) { \
    (F) |= (SF);        \
}

#define ClearFlag(F,SF) { \
    (F) &= ~(SF);         \
}

#define QuadAlign(P) (             \
    ((((P)) + 7) & (-8)) \
)

//
//  Turn on pseudo-asserts if CC_FREE_ASSERTS is defined.
//

#if (!DBG && defined( CC_FREE_ASSERTS ))
#undef ASSERT
#undef ASSERTMSG
#define ASSERT(exp)                                             \
    ((exp) ? TRUE :                                             \
             (DbgPrint( "%s:%d %s\n",__FILE__,__LINE__,#exp ),  \
              DbgBreakPoint(),                                  \
              TRUE))
#define ASSERTMSG(msg,exp)                                              \
    ((exp) ? TRUE :                                                     \
             (DbgPrint( "%s:%d %s %s\n",__FILE__,__LINE__,msg,#exp ),   \
              DbgBreakPoint(),                                          \
              TRUE))
#endif


//
//  Define the Virtual Address Control Block, which controls all mapping
//  performed by the Cache Manager.
//

//
//  First some constants
//

#define PREALLOCATED_VACBS               (4)

//
//  Virtual Address Control Block
//

typedef struct _VACB {

    //
    //  Base Address for this control block.
    //

    PVOID BaseAddress;

    //
    //  Pointer to the Shared Cache Map using this Vacb.
    //

    struct _SHARED_CACHE_MAP *SharedCacheMap;

    //
    //  Overlay for remembering mapped offset within the Shared Cache Map,
    //  and the count of the number of times this Vacb is in use.
    //

    union {

        //
        //  File Offset within Shared Cache Map
        //

        LARGE_INTEGER FileOffset;

        //
        //  Count of number of times this Vacb is in use.  The size of this
        //  count is calculated to be adequate, while never large enough to
        //  overwrite nonzero bits of the FileOffset, which is a multiple
        //  of VACB_MAPPING_GRANULARITY.
        //

        USHORT ActiveCount;

    } Overlay;

    //
    //  Entry for the VACB reuse list
    //

    LIST_ENTRY LruList;

} VACB, *PVACB;

//
//  These define special flag values that are overloaded as PVACB.  They cause
//  certain special behavior, currently only in the case of multilevel structures.
//

#define VACB_SPECIAL_REFERENCE           ((PVACB) ~0)
#define VACB_SPECIAL_DEREFERENCE         ((PVACB) ~1)

#define VACB_SPECIAL_FIRST_VALID         VACB_SPECIAL_DEREFERENCE



#define PRIVATE_CACHE_MAP_READ_AHEAD_ACTIVE     0x10000
#define PRIVATE_CACHE_MAP_READ_AHEAD_ENABLED    0x20000

typedef struct _PRIVATE_CACHE_MAP_FLAGS {
    ULONG DontUse : 16;                     // Overlaid with NodeTypeCode

    //
    //  This flag says read ahead is currently active, which means either
    //  a file system call to CcReadAhead is still determining if the
    //  desired data is already resident, or else a request to do read ahead
    //  has been queued to a worker thread.
    //

    ULONG ReadAheadActive : 1;

    //
    //  Flag to say whether read ahead is currently enabled for this
    //  FileObject/PrivateCacheMap.  On read misses it is enabled on
    //  read ahead hits it will be disabled.  Initially disabled.
    //

    ULONG ReadAheadEnabled : 1;

    ULONG Available : 14;
} PRIVATE_CACHE_MAP_FLAGS;

#define CC_SET_PRIVATE_CACHE_MAP(PrivateCacheMap, Flags) \
    RtlInterlockedSetBitsDiscardReturn (&PrivateCacheMap->UlongFlags, Flags);

#define CC_CLEAR_PRIVATE_CACHE_MAP(PrivateCacheMap, Feature) \
    RtlInterlockedAndBitsDiscardReturn (&PrivateCacheMap->UlongFlags, (ULONG)~Feature);

//
//  The Private Cache Map is a structure pointed to by the File Object, whenever
//  a file is opened with caching enabled (default).
//

typedef struct _PRIVATE_CACHE_MAP {

    //
    //  Type and size of this record
    //

    union {
        CSHORT NodeTypeCode;
        PRIVATE_CACHE_MAP_FLAGS Flags;
        ULONG UlongFlags;
    };

    //
    //  Read Ahead mask formed from Read Ahead granularity - 1.
    //  Private Cache Map ReadAheadSpinLock controls access to this field.
    //

    ULONG ReadAheadMask;

    //
    //  Pointer to FileObject for this PrivateCacheMap.
    //

    PFILE_OBJECT FileObject;

    //
    //  READ AHEAD CONTROL
    //
    //  Read ahead history for determining when read ahead might be
    //  beneficial.
    //

    LARGE_INTEGER FileOffset1;
    LARGE_INTEGER BeyondLastByte1;

    LARGE_INTEGER FileOffset2;
    LARGE_INTEGER BeyondLastByte2;

    //
    //  Current read ahead requirements.
    //
    //  Array element 0 is optionally used for recording remaining bytes
    //  required for satisfying a large Mdl read.
    //
    //  Array element 1 is used for predicted read ahead.
    //

    LARGE_INTEGER ReadAheadOffset[2];
    ULONG ReadAheadLength[2];

    //
    //  SpinLock controlling access to following fields
    //

    KSPIN_LOCK ReadAheadSpinLock;

    //
    // Links for list of all PrivateCacheMaps linked to the same
    // SharedCacheMap.
    //

    LIST_ENTRY PrivateLinks;

} PRIVATE_CACHE_MAP;

typedef PRIVATE_CACHE_MAP *PPRIVATE_CACHE_MAP;


//
//  The Shared Cache Map is a per-file structure pointed to indirectly by
//  each File Object.  The File Object points to a pointer in a single
//  FS-private structure for the file (Fcb).  The SharedCacheMap maps the
//  first part of the file for common access by all callers.
//

//
//  OpenCount log Reasons/Actions
//

#if OPEN_COUNT_LOG
typedef struct _CC_OPEN_COUNT_LOG_ENTRY {
    ULONG Action;
    ULONG Reason;
} CC_OPEN_COUNT_LOG_ENTRY;

typedef struct _CC_OPEN_COUNT_LOG {
    USHORT Next;
    USHORT Size;
    CC_OPEN_COUNT_LOG_ENTRY Log[48];
} CC_OPEN_COUNT_LOG;

#define CcAddOpenToLog( LOG, ACTION, REASON ) {             \
    (LOG)->Log[(LOG)->Next].Action = (ACTION);              \
    (LOG)->Log[(LOG)->Next].Reason = (REASON);              \
    (LOG)->Next += 1;                                       \
    if ((LOG)->Next == (LOG)->Size) {                       \
        (LOG)->Next = 0;                                    \
    }                                                       \
}
#else  // OPEN_COUNT_LOG
#define CcAddOpenToLog( LOG, ACTION, REASON )
#endif // OPEN_COUNT_LOG

#define CcIncrementOpenCount( SCM, REASON ) {               \
    (SCM)->OpenCount += 1;                                  \
    if (REASON != 0) {                                      \
        CcAddOpenToLog( &(SCM)->OpenCountLog, REASON, 1 );  \
    }                                                       \
}

#define CcDecrementOpenCount( SCM, REASON ) {               \
    (SCM)->OpenCount -= 1;                                  \
    if (REASON != 0) {                                      \
        CcAddOpenToLog( &(SCM)->OpenCountLog, REASON, -1 ); \
    }                                                       \
}

typedef struct _SHARED_CACHE_MAP {

    //
    //  Type and size of this record
    //

    CSHORT NodeTypeCode;
    CSHORT NodeByteSize;

    //
    //  Number of times this file has been opened cached.
    //

    ULONG OpenCount;

    //
    //  Actual size of file, primarily for restricting Read Ahead.  Initialized
    //  on creation and maintained by extend and truncate operations.
    //
    //  NOTE:   This field may never be moved, thanks to the late DavidGoe,
    //          who should have written this comment himself :-(   cache.h
    //          exports a macro which "knows" that FileSize is the second
    //          longword in the Cache Map!
    //

    LARGE_INTEGER FileSize;

    //
    //  Bcb Listhead.  The BcbList is ordered by descending
    //  FileOffsets, to optimize misses in the sequential I/O case.
    //  Synchronized by the BcbSpinLock.
    //

    LIST_ENTRY BcbList;

    //
    //  Size of section created.
    //

    LARGE_INTEGER SectionSize;

    //
    //  ValidDataLength for file, as currently stored by the file system.
    //  Synchronized by the BcbSpinLock or exclusive access by FileSystem.
    //

    LARGE_INTEGER ValidDataLength;

    //
    //  Goal for ValidDataLength, when current dirty data is written.
    //  Synchronized by the BcbSpinLock or exclusive access by FileSystem.
    //

    LARGE_INTEGER ValidDataGoal;

    //
    //  Pointer to a contiguous array of Vacb pointers which control mapping
    //  to this file, along with Vacbs (currently) for a 1MB file.
    //  Synchronized by CcVacbSpinLock.
    //

    PVACB InitialVacbs[PREALLOCATED_VACBS];
    PVACB * Vacbs;

    //
    //  Referenced pointer to original File Object on which the SharedCacheMap
    //  was created.
    //

    PFILE_OBJECT FileObject;

    //
    //  Describe Active Vacb and Page for copysup optimizations.
    //

    volatile PVACB ActiveVacb;

    //
    //  Virtual address needing zero to end of page
    //

    volatile PVOID NeedToZero;

    ULONG ActivePage;
    ULONG NeedToZeroPage;

    //
    //  Fields for synchronizing on active requests.
    //

    KSPIN_LOCK ActiveVacbSpinLock;
    ULONG VacbActiveCount;

    //
    //  Number of dirty pages in this SharedCacheMap.  Used to trigger
    //  write behind.  Synchronized by CcMasterSpinLock.
    //

    ULONG DirtyPages;

    //
    //  THE NEXT TWO FIELDS MUST BE ADJACENT, TO SUPPORT
    //  SHARED_CACHE_MAP_LIST_CURSOR!
    //
    //  Links for Global SharedCacheMap List
    //

    LIST_ENTRY SharedCacheMapLinks;

    //
    //  Shared Cache Map flags (defined below)
    //

    ULONG Flags;

    //
    //  Status variable set by creator of SharedCacheMap
    //

    NTSTATUS Status;

    //
    //  Mask Bcb for this SharedCacheMap, if there is one.
    //  Synchronized by the BcbSpinLock.
    //

    struct _MBCB *Mbcb;

    //
    //  Pointer to the common Section Object used by the file system.
    //

    PVOID Section;

    //
    //  This event pointer is used to handle creation collisions.
    //  If a second thread tries to call CcInitializeCacheMap for the
    //  same file, while BeingCreated (below) is TRUE, then that thread
    //  will allocate an event store it here (if not already allocated),
    //  and wait on it.  The first creator will set this event when it
    //  is done.  The event is not deleted until CcUninitializedCacheMap
    //  is called, to avoid possible race conditions.  (Note that normally
    //  the event never has to be allocated.
    //

    PKEVENT CreateEvent;

    //
    //  This points to an event used to wait for active count to go to zero
    //

    PKEVENT WaitOnActiveCount;

    //
    //  These two fields control the writing of large metadata
    //  streams.  The first field gives a target for the current
    //  flush interval, and the second field stores the end of
    //  the last flush that occurred on this file.
    //

    ULONG PagesToWrite;
    LONGLONG BeyondLastFlush;

    //
    //  Pointer to structure of routines used by the Lazy Writer to Acquire
    //  and Release the file for Lazy Write and Close, to avoid deadlocks,
    //  and the context to call them with.
    //

    PCACHE_MANAGER_CALLBACKS Callbacks;

    PVOID LazyWriteContext;

    //
    //  Listhead of all PrivateCacheMaps linked to this SharedCacheMap.
    //

    LIST_ENTRY PrivateList;

    //
    //  Log handle specified for this shared cache map, for support of routines
    //  in logsup.c
    //

    PVOID LogHandle;

    //
    //  Callback routine specified for flushing to Lsn.
    //

    PFLUSH_TO_LSN FlushToLsnRoutine;

    //
    //  Dirty Page Threshold for this stream
    //

    ULONG DirtyPageThreshold;

    //
    //  Lazy Writer pass count.  Used by the Lazy Writer for
    //  no modified write streams, which are not serviced on
    //  every pass in order to avoid contention with foreground
    //  activity.
    //

    ULONG LazyWritePassCount;

    //
    //  This event pointer is used to allow a file system to be notified when
    //  the deletion of a shared cache map.
    //
    //  This has to be provided here because the cache manager may decide to
    //  "Lazy Delete" the shared cache map, and some network file systems
    //  will want to know when the lazy delete completes.
    //

    PCACHE_UNINITIALIZE_EVENT UninitializeEvent;

    //
    //  This Vacb pointer is needed for keeping the NeedToZero virtual address
    //  valid.
    //

    PVACB NeedToZeroVacb;

    //
    //  Spinlock for synchronizing the Mbcb and Bcb lists - must be acquired
    //  before CcMasterSpinLock.  This spinlock also synchronizes ValidDataGoal
    //  and ValidDataLength, as described above.
    //

    KSPIN_LOCK BcbSpinLock;

    PVOID Reserved;

    //
    //  This is an event which may be used for the WaitOnActiveCount event.  We
    //  avoid overhead by only "activating" it when it is needed.
    //

    KEVENT Event;

    EX_PUSH_LOCK VacbPushLock;
    
    //
    //  Preallocate one PrivateCacheMap to reduce pool allocations.
    //

    PRIVATE_CACHE_MAP PrivateCacheMap;

#if OPEN_COUNT_LOG

    //
    //  Instrument reasons for OpenCount
    //

    CC_OPEN_COUNT_LOG OpenCountLog;

#endif

} SHARED_CACHE_MAP;

typedef SHARED_CACHE_MAP *PSHARED_CACHE_MAP;

//
//  Shared Cache Map Flags
//

//
//  Read ahead has been disabled on this file.
//

#define DISABLE_READ_AHEAD               0x0001

//
//  Write behind has been disabled on this file.
//

#define DISABLE_WRITE_BEHIND             0x0002

//
//  This flag indicates whether CcInitializeCacheMap was called with
//  PinAccess = TRUE.
//

#define PIN_ACCESS                       0x0004

//
//  This flag indicates that a truncate is required when OpenCount
//  goes to 0.
//

#define TRUNCATE_REQUIRED                0x0010

//
//  This flag indicates that a LazyWrite request is queued.
//

#define WRITE_QUEUED                     0x0020

//
//  This flag indicates that we have never seen anyone cache
//  the file except for with FO_SEQUENTIAL_ONLY, so we should
//  tell MM to quickly dump pages when we unmap.
//

#define ONLY_SEQUENTIAL_ONLY_SEEN        0x0040

//
//  Active Page is locked
//

#define ACTIVE_PAGE_IS_DIRTY             0x0080

//
//  Flag to say that a create is in progress.
//

#define BEING_CREATED                    0x0100

//
//  Flag to say that modified write was disabled on the section.
//

#define MODIFIED_WRITE_DISABLED          0x0200

//
//  Flag that indicates if a lazy write ever occurred on this file.
//

#define LAZY_WRITE_OCCURRED              0x0400

//
//  Flag that indicates this structure is only a cursor, only the
//  SharedCacheMapLinks and Flags are valid!
//

#define IS_CURSOR                        0x0800

//
//  Flag that indicates that we have seen someone cache this file
//  and specify FO_RANDOM_ACCESS.  This will deactivate our cache
//  working set trim assist.
//

#define RANDOM_ACCESS_SEEN               0x1000

//
//  Flag indicating that the stream is private write.  This disables
//  non-aware flush/purge.
//

#define PRIVATE_WRITE                    0x2000

//
//  Cursor structure for traversing the SharedCacheMap lists.  Anyone
//  scanning these lists must verify that the IS_CURSOR flag is clear
//  before looking at other SharedCacheMap fields.
//


typedef struct _SHARED_CACHE_MAP_LIST_CURSOR {

    //
    //  Links for Global SharedCacheMap List
    //

    LIST_ENTRY SharedCacheMapLinks;

    //
    //  Shared Cache Map flags, IS_CURSOR must be set.
    //

    ULONG Flags;

} SHARED_CACHE_MAP_LIST_CURSOR, *PSHARED_CACHE_MAP_LIST_CURSOR;



#ifndef KDEXT
//
//  Bitmap Range structure.  For small files there is just one embedded in the
//  Mbcb.  For large files there may be many of these linked to the Mbcb.
//

typedef struct _BITMAP_RANGE {

    //
    //  Links for the list of bitmap ranges off the Mbcb.
    //

    LIST_ENTRY Links;

    //
    //  Base page (FileOffset / PAGE_SIZE) represented by this range.
    //  (Size is a fixed maximum.)
    //

    LONGLONG BasePage;

    //
    //  First and Last dirty pages relative to the BasePage.
    //

    ULONG FirstDirtyPage;
    ULONG LastDirtyPage;

    //
    //  Number of dirty pages in this range.
    //

    ULONG DirtyPages;

    //
    //  Pointer to the bitmap for this range.
    //

    PULONG Bitmap;

} BITMAP_RANGE, *PBITMAP_RANGE;
#endif

//
//  This structure is a "mask" Bcb.  For fast simple write operations,
//  a mask Bcb is used so that we basically only have to set bits to remember
//  where the dirty data is.
//

typedef struct _MBCB {

    //
    //  Type and size of this record
    //

    CSHORT NodeTypeCode;
    CSHORT NodeIsInZone;

    //
    //  This field is used as a scratch area for the Lazy Writer to
    //  guide how much he will write each time he wakes up.
    //

    ULONG PagesToWrite;

    //
    //  Number of dirty pages (set bits) in the bitmap below.
    //

    ULONG DirtyPages;

    //
    //  Reserved for alignment.
    //

    ULONG Reserved;

    //
    //  ListHead of Bitmap ranges.
    //

    LIST_ENTRY BitmapRanges;

    //
    //  This is a hint on where to resume writing, since we will not
    //  always write all of the dirty data at once.
    //

    LONGLONG ResumeWritePage;

    //
    //  Initial three embedded Bitmap ranges.  For a file up to 2MB, only the
    //  first range is used, and the rest of the Mbcb contains bits for 2MB of
    //  dirty pages (4MB on Alpha).  For larger files, all three ranges may
    //  be used to describe external bitmaps.
    //

    BITMAP_RANGE BitmapRange1;
    BITMAP_RANGE BitmapRange2;
    BITMAP_RANGE BitmapRange3;

} MBCB;

typedef MBCB *PMBCB;


//
//  This is the Buffer Control Block structure for representing data which
//  is "pinned" in memory by one or more active requests and/or dirty.  This
//  structure is created the first time that a call to CcPinFileData specifies
//  a particular integral range of pages.  It is deallocated whenever the Pin
//  Count reaches 0 and the Bcb is not Dirty.
//
//  NOTE: The first four fields must be the same as the PUBLIC_BCB.
//

typedef struct _BCB {

    union {

        //
        // To ensure QuadAlign (sizeof (BCB)) >= QuadAlign (sizeof (MBCB))
        // so that they can share the same pool blocks.
        //

        MBCB Dummy;

        struct {

            //
            //  Type and size of this record
            //

            CSHORT NodeTypeCode;

            //
            //  Flags
            //

            BOOLEAN Dirty;
            BOOLEAN Reserved;

            //
            //  Byte FileOffset and and length of entire buffer
            //

            ULONG  ByteLength;
            LARGE_INTEGER FileOffset;

            //
            //  Links for BcbList in SharedCacheMap
            //

            LIST_ENTRY BcbLinks;

            //
            //  Byte FileOffset of last byte in buffer (used for searching)
            //

            LARGE_INTEGER BeyondLastByte;

            //
            //  Oldest Lsn (if specified) when this buffer was set dirty.
            //

            LARGE_INTEGER OldestLsn;

            //
            //  Most recent Lsn specified when this buffer was set dirty.
            //  The FlushToLsnRoutine is called with this Lsn.
            //

            LARGE_INTEGER NewestLsn;

            //
            //  Pointer to Vacb via which this Bcb is mapped.
            //

            PVACB Vacb;

#if LIST_DBG
            //
            //  Links and caller addresses for the global Bcb list (for debug only)
            //

            LIST_ENTRY CcBcbLinks;
            PVOID CallerAddress;
            PVOID CallersCallerAddress;
#endif

            //
            //  Count of threads actively using this Bcb to process a request.
            //  This must be manipulated under protection of the BcbListSpinLock
            //  in the SharedCacheMap.
            //

            ULONG PinCount;

            //
            //  Resource to synchronize buffer access.  Pinning Readers and all Writers
            //  of the described buffer take out shared access (synchronization of
            //  buffer modifications is strictly up to the caller).  Note that pinning
            //  readers do not declare if they are going to modify the buffer or not.
            //  Anyone writing to disk takes out exclusive access, to prevent the buffer
            //  from changing while it is being written out.
            //

            ERESOURCE Resource;

            //
            //  Pointer to SharedCacheMap for this Bcb.
            //

            PSHARED_CACHE_MAP SharedCacheMap;

            //
            //  This is the Base Address at which the buffer can be seen in
            //  system space.  All access to buffer data should go through this
            //  address.
            //

            PVOID BaseAddress;
        };
    };

} BCB;

#ifndef KDEXT
typedef BCB *PBCB;
#endif

//
//  This is the Overlap Buffer Control Block structure for representing data which
//  is "pinned" in memory and must be represented by multiple Bcbs due to overlaps.
//
//  NOTE: The first four fields must be the same as the PUBLIC_BCB.
//

typedef struct _OBCB {

    //
    //  Type and size of this record
    //

    CSHORT NodeTypeCode;
    CSHORT NodeByteSize;

    //
    //  Byte FileOffset and and length of entire buffer
    //

    ULONG  ByteLength;
    LARGE_INTEGER FileOffset;

    //
    //  Vector of Bcb pointers.
    //

    PBCB Bcbs[ANYSIZE_ARRAY];

} OBCB;

typedef OBCB *POBCB;


//
//  Struct for remembering deferred writes for later posting.
//

typedef struct _DEFERRED_WRITE {

    //
    //  Type and size of this record
    //

    CSHORT NodeTypeCode;
    CSHORT NodeByteSize;

    //
    //  The file to be written.
    //

    PFILE_OBJECT FileObject;

    //
    //  Number of bytes the caller intends to write
    //

    ULONG BytesToWrite;

    //
    //  Links for the deferred write queue.
    //

    LIST_ENTRY DeferredWriteLinks;

    //
    //  If this event pointer is not NULL, then this event will
    //  be signalled when the write is ok, rather than calling
    //  the PostRoutine below.
    //

    PKEVENT Event;

    //
    //  The posting routine and its parameters
    //

    PCC_POST_DEFERRED_WRITE PostRoutine;
    PVOID Context1;
    PVOID Context2;

    BOOLEAN LimitModifiedPages;

} DEFERRED_WRITE, *PDEFERRED_WRITE;


//
//  Struct controlling the Lazy Writer algorithms
//

typedef struct _LAZY_WRITER {

    //
    //  Work queue.
    //

    LIST_ENTRY WorkQueue;

    //
    //  Dpc and Timer Structures used for activating periodic scan when active.
    //

    KDPC ScanDpc;
    KTIMER ScanTimer;

    //
    //  Boolean to say whether Lazy Writer scan is active or not.
    //

    BOOLEAN ScanActive;

    //
    //  Boolean indicating if there is any other reason for Lazy Writer to
    //  wake up.
    //

    BOOLEAN OtherWork;

} LAZY_WRITER;


#ifndef KDEXT
//
//  Work queue entry for the worker threads, with an enumerated
//  function code.
//

typedef enum _WORKER_FUNCTION {
    Noop = 0,
    ReadAhead,
    WriteBehind,
    LazyWriteScan,
    EventSet
} WORKER_FUNCTION;
#endif

typedef struct _WORK_QUEUE_ENTRY {

    //
    //  List entry for our work queues.
    //

    LIST_ENTRY WorkQueueLinks;

    //
    //  Define a union to contain function-specific parameters.
    //

    union {

        //
        //  Read parameters (for read ahead)
        //

        struct {
            PFILE_OBJECT FileObject;
        } Read;

        //
        //  Write parameters (for write behind)
        //

        struct {
            PSHARED_CACHE_MAP SharedCacheMap;
        } Write;

        //
        //  Set event parameters (for queue checks)
        //

        struct {
            PKEVENT Event;
        } Event;

    } Parameters;

    //
    //  Function code for this entry:
    //

    UCHAR Function;

} WORK_QUEUE_ENTRY, *PWORK_QUEUE_ENTRY;

//
//  This is a structure apended to the end of an MDL
//

typedef struct _MDL_WRITE {

    //
    //  This field is for the use of the Server to stash anything interesting
    //

    PVOID ServerContext;

    //
    //  This is the resource to release when the write is complete.
    //

    PERESOURCE Resource;

    //
    //  This is thread caller's thread, and the thread that must release
    //  the resource.
    //

    ERESOURCE_THREAD Thread;

    //
    //  This links all the pending MDLs through the shared cache map.
    //

    LIST_ENTRY MdlLinks;

} MDL_WRITE, *PMDL_WRITE;


//
//  Common Private routine definitions for the Cache Manager
//

VOID
CcGetActiveVacb (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    OUT PVACB *Vacb,
    OUT PULONG Page,
    OUT PULONG Dirty
    );

VOID
CcSetActiveVacb (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN OUT PVACB *Vacb,
    IN ULONG Page,
    IN ULONG Dirty
    );

//
//  We trim out the previous macro-forms of Get/Set (nondpc) so that we can page
//  more cache manager code that otherwise does not acquire spinlocks.
//

#define GetActiveVacb(SCM,IRQ,V,P,D)     CcGetActiveVacb((SCM),&(V),&(P),&(D))
#define SetActiveVacb(SCM,IRQ,V,P,D)     CcSetActiveVacb((SCM),&(V),(P),(D))

#define GetActiveVacbAtDpcLevel(SCM,V,P,D) {                            \
    ExAcquireSpinLockAtDpcLevel(&(SCM)->ActiveVacbSpinLock);            \
    (V) = (SCM)->ActiveVacb;                                            \
    if ((V) != NULL) {                                                  \
        (P) = (SCM)->ActivePage;                                        \
        (SCM)->ActiveVacb = NULL;                                       \
        (D) = (SCM)->Flags & ACTIVE_PAGE_IS_DIRTY;                      \
    }                                                                   \
    ExReleaseSpinLockFromDpcLevel(&(SCM)->ActiveVacbSpinLock);          \
}

//
//  Gather the common work of charging and deducting dirty page counts.  When
//  write hysteresis was being considered during Windows XP, this also helped
//  gather up the activation of that throttle.
//

#define CcDeductDirtyPages( S, P )                                      \
        CcTotalDirtyPages -= (P);                                       \
        (S)->DirtyPages -= (P);
        
#define CcChargeMaskDirtyPages( S, M, B, P )                            \
        CcTotalDirtyPages += (P);                                       \
        (M)->DirtyPages += (P);                                         \
        (B)->DirtyPages += (P);                                         \
        (S)->DirtyPages += (P);

#define CcChargePinDirtyPages( S, P )                                   \
        CcTotalDirtyPages += (P);                                       \
        (S)->DirtyPages += (P);

VOID
CcPostDeferredWrites (
    );

BOOLEAN
CcPinFileData (
    IN PFILE_OBJECT FileObject,
    IN PLARGE_INTEGER FileOffset,
    IN ULONG Length,
    IN BOOLEAN ReadOnly,
    IN BOOLEAN WriteOnly,
    IN ULONG Flags,
    OUT PBCB *Bcb,
    OUT PVOID *BaseAddress,
    OUT PLARGE_INTEGER BeyondLastByte
    );

typedef enum {
    UNPIN,
    UNREF,
    SET_CLEAN
} UNMAP_ACTIONS;

VOID
FASTCALL
CcUnpinFileData (
    IN OUT PBCB Bcb,
    IN BOOLEAN ReadOnly,
    IN UNMAP_ACTIONS UnmapAction
    );

VOID
FASTCALL
CcDeallocateBcb (
    IN PBCB Bcb
    );

VOID
FASTCALL
CcPerformReadAhead (
    IN PFILE_OBJECT FileObject
    );

VOID
CcSetDirtyInMask (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN PLARGE_INTEGER FileOffset,
    IN ULONG Length
    );

VOID
FASTCALL
CcWriteBehind (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN PIO_STATUS_BLOCK IoStatus
    );

#define ZERO_FIRST_PAGE                  1
#define ZERO_MIDDLE_PAGES                2
#define ZERO_LAST_PAGE                   4

BOOLEAN
CcMapAndRead(
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN PLARGE_INTEGER FileOffset,
    IN ULONG Length,
    IN ULONG ZeroFlags,
    IN BOOLEAN Wait,
    IN PVOID BaseAddress
    );

VOID
CcFreeActiveVacb (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN PVACB ActiveVacb OPTIONAL,
    IN ULONG ActivePage,
    IN ULONG PageIsDirty
    );

VOID
CcMapAndCopy(
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN PVOID UserBuffer,
    IN PLARGE_INTEGER FileOffset,
    IN ULONG Length,
    IN ULONG ZeroFlags,
    IN PFILE_OBJECT FileObject
    );

VOID
CcScanDpc (
    IN PKDPC Dpc,
    IN PVOID DeferredContext,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2
    );

VOID
CcScheduleLazyWriteScan (
    IN BOOLEAN FastScan
    );

VOID
CcStartLazyWriter (
    IN PVOID NotUsed
    );

#define CcAllocateWorkQueueEntry() \
    (PWORK_QUEUE_ENTRY)ExAllocateFromPPLookasideList(LookasideTwilightList)

#define CcFreeWorkQueueEntry(_entry_)         \
    ExFreeToPPLookasideList(LookasideTwilightList, (_entry_))

VOID
FASTCALL
CcPostWorkQueue (
    IN PWORK_QUEUE_ENTRY WorkQueueEntry,
    IN PLIST_ENTRY WorkQueue
    );

VOID
CcWorkerThread (
    PVOID ExWorkQueueItem
    );

VOID
FASTCALL
CcDeleteSharedCacheMap (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN KIRQL ListIrql,
    IN ULONG ReleaseFile
    );

//
//  This exception filter handles STATUS_IN_PAGE_ERROR correctly
//

LONG
CcCopyReadExceptionFilter(
    IN PEXCEPTION_POINTERS ExceptionPointer,
    IN PNTSTATUS ExceptionCode
    );

//
//  Exception filter for Worker Threads in lazyrite.c
//

LONG
CcExceptionFilter (
    IN NTSTATUS ExceptionCode
    );

#ifdef CCDBG
VOID
CcDump (
    IN PVOID Ptr
    );
#endif

//
//  Vacb routines
//

VOID
CcInitializeVacbs(
    );

PVOID
CcGetVirtualAddressIfMapped (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN LONGLONG FileOffset,
    OUT PVACB *Vacb,
    OUT PULONG ReceivedLength
    );

PVOID
CcGetVirtualAddress (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN LARGE_INTEGER FileOffset,
    OUT PVACB *Vacb,
    OUT PULONG ReceivedLength
    );

VOID
FASTCALL
CcFreeVirtualAddress (
    IN PVACB Vacb
    );

VOID
CcReferenceFileOffset (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN LARGE_INTEGER FileOffset
    );

VOID
CcDereferenceFileOffset (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN LARGE_INTEGER FileOffset
    );

VOID
CcWaitOnActiveCount (
    IN PSHARED_CACHE_MAP SharedCacheMap
    );

NTSTATUS
FASTCALL
CcCreateVacbArray (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN LARGE_INTEGER NewSectionSize
    );

NTSTATUS
CcExtendVacbArray (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN LARGE_INTEGER NewSectionSize
    );

BOOLEAN
FASTCALL
CcUnmapVacbArray (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN PLARGE_INTEGER FileOffset OPTIONAL,
    IN ULONG Length,
    IN BOOLEAN UnmapBehind
    );

VOID
CcAdjustVacbLevelLockCount (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN LONGLONG FileOffset,
    IN LONG Adjustment
    );

PLIST_ENTRY
CcGetBcbListHeadLargeOffset (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN LONGLONG FileOffset,
    IN BOOLEAN FailToSuccessor
    );

ULONG
CcPrefillVacbLevelZone (
    IN ULONG NumberNeeded,
    OUT PKIRQL OldIrql,
    IN ULONG NeedBcbListHeads
    );

VOID
CcDrainVacbLevelZone (
    );

//
//  Define references to global data
//

extern KSPIN_LOCK CcBcbSpinLock;
extern LIST_ENTRY CcCleanSharedCacheMapList;
extern SHARED_CACHE_MAP_LIST_CURSOR CcDirtySharedCacheMapList;
extern SHARED_CACHE_MAP_LIST_CURSOR CcLazyWriterCursor;
extern GENERAL_LOOKASIDE CcTwilightLookasideList;
extern ULONG CcNumberWorkerThreads;
extern ULONG CcNumberActiveWorkerThreads;
extern LIST_ENTRY CcIdleWorkerThreadList;
extern LIST_ENTRY CcExpressWorkQueue;
extern LIST_ENTRY CcRegularWorkQueue;
extern LIST_ENTRY CcPostTickWorkQueue;
extern BOOLEAN CcQueueThrottle;
extern ULONG CcIdleDelayTick;
extern LARGE_INTEGER CcNoDelay;
extern LARGE_INTEGER CcFirstDelay;
extern LARGE_INTEGER CcIdleDelay;
extern LARGE_INTEGER CcCollisionDelay;
extern LARGE_INTEGER CcTargetCleanDelay;
extern LAZY_WRITER LazyWriter;
extern ULONG_PTR CcNumberVacbs;
extern PVACB CcVacbs;
extern PVACB CcBeyondVacbs;
extern LIST_ENTRY CcVacbLru;
extern LIST_ENTRY CcVacbFreeList;
extern KSPIN_LOCK CcDeferredWriteSpinLock;
extern LIST_ENTRY CcDeferredWrites;
extern ULONG CcDirtyPageThreshold;
extern ULONG CcDirtyPageTarget;
extern ULONG CcDirtyPagesLastScan;
extern ULONG CcPagesYetToWrite;
extern ULONG CcPagesWrittenLastTime;
extern ULONG CcThrottleLastTime;
extern ULONG CcDirtyPageHysteresisThreshold;
extern PSHARED_CACHE_MAP CcSingleDirtySourceDominant;
extern ULONG CcAvailablePagesThreshold;
extern ULONG CcTotalDirtyPages;
extern ULONG CcTune;
extern LONG CcAggressiveZeroCount;
extern LONG CcAggressiveZeroThreshold;
extern ULONG CcLazyWriteHotSpots;
extern MM_SYSTEMSIZE CcCapturedSystemSize;
extern ULONG CcMaxVacbLevelsSeen;
extern ULONG CcVacbLevelEntries;
extern PVACB *CcVacbLevelFreeList;
extern ULONG CcVacbLevelWithBcbsEntries;
extern PVACB *CcVacbLevelWithBcbsFreeList;

//
//  Macros for allocating and deallocating Vacb levels - CcVacbSpinLock must
//  be acquired.
//

_inline PVACB *CcAllocateVacbLevel (
    IN LOGICAL AllocatingBcbListHeads
    )

{
    PVACB *ReturnEntry;

    if (AllocatingBcbListHeads) {
        ReturnEntry = CcVacbLevelWithBcbsFreeList;
        CcVacbLevelWithBcbsFreeList = (PVACB *)*ReturnEntry;
        CcVacbLevelWithBcbsEntries -= 1;
    } else {
        ReturnEntry = CcVacbLevelFreeList;
        CcVacbLevelFreeList = (PVACB *)*ReturnEntry;
        CcVacbLevelEntries -= 1;
    }
    *ReturnEntry = NULL;
    ASSERT(RtlCompareMemory(ReturnEntry, ReturnEntry + 1, VACB_LEVEL_BLOCK_SIZE - sizeof(PVACB)) ==
                                                          (VACB_LEVEL_BLOCK_SIZE - sizeof(PVACB)));
    return ReturnEntry;
}

_inline VOID CcDeallocateVacbLevel (
    IN PVACB *Entry,
    IN LOGICAL DeallocatingBcbListHeads
    )

{
    if (DeallocatingBcbListHeads) {
        *Entry = (PVACB)CcVacbLevelWithBcbsFreeList;
        CcVacbLevelWithBcbsFreeList = Entry;
        CcVacbLevelWithBcbsEntries += 1;
    } else {
        *Entry = (PVACB)CcVacbLevelFreeList;
        CcVacbLevelFreeList = Entry;
        CcVacbLevelEntries += 1;
    }
}

//
//  Export the macros for inspecting the reference counts for
//  the multilevel Vacb array.
//

_inline
PVACB_LEVEL_REFERENCE
VacbLevelReference (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN PVACB *VacbArray,
    IN ULONG Level
    )
{
    return (PVACB_LEVEL_REFERENCE)
           ((PCHAR)VacbArray +
            VACB_LEVEL_BLOCK_SIZE +
            (Level != 0?
             0 : (FlagOn( SharedCacheMap->Flags, MODIFIED_WRITE_DISABLED )?
                  VACB_LEVEL_BLOCK_SIZE : 0)));
}

_inline
ULONG
IsVacbLevelReferenced (
    IN PSHARED_CACHE_MAP SharedCacheMap,
    IN PVACB *VacbArray,
    IN ULONG Level
    )
{
    PVACB_LEVEL_REFERENCE VacbReference = VacbLevelReference( SharedCacheMap, VacbArray, Level );

    return VacbReference->Reference | VacbReference->SpecialReference;
}


//
//  Here is a page of macros stolen directly from Pinball...
//

//
//  The following macros are used to establish the semantics needed
//  to do a return from within a try-finally clause.  As a rule every
//  try clause must end with a label call try_exit.  For example,
//
//      try {
//              :
//              :
//
//      try_exit: NOTHING;
//      } finally {
//
//              :
//              :
//      }
//
//  Every return statement executed inside of a try clause should use the
//  try_return macro.  If the compiler fully supports the try-finally construct
//  then the macro should be
//
//      #define try_return(S)  { return(S); }
//
//  If the compiler does not support the try-finally construct then the macro
//  should be
//
//      #define try_return(S)  { S; goto try_exit; }
//

#define try_return(S) { S; goto try_exit; }

#ifdef CCDBG

extern LONG CcDebugTraceLevel;
extern LONG CcDebugTraceIndent;

#ifndef CCDBG_LOCK

#define DebugTrace(INDENT,LEVEL,X,Y) {                     \
    LONG _i;                                               \
    if (((LEVEL) == 0) || (CcDebugTraceLevel & (LEVEL))) { \
        _i = (ULONG)PsGetCurrentThread();                  \
        DbgPrint("%08lx:",_i);                             \
        if ((INDENT) < 0) {                                \
            CcDebugTraceIndent += (INDENT);                \
        }                                                  \
        if (CcDebugTraceIndent < 0) {                      \
            CcDebugTraceIndent = 0;                        \
        }                                                  \
        for (_i=0; _i<CcDebugTraceIndent; _i+=1) {         \
            DbgPrint(" ");                                 \
        }                                                  \
        DbgPrint(X,Y);                                     \
        if ((INDENT) > 0) {                                \
            CcDebugTraceIndent += (INDENT);                \
        }                                                  \
    }                                                      \
}

#define DebugTrace2(INDENT,LEVEL,X,Y,Z) {                  \
    LONG _i;                                               \
    if (((LEVEL) == 0) || (CcDebugTraceLevel & (LEVEL))) { \
        _i = (ULONG)PsGetCurrentThread();                  \
        DbgPrint("%08lx:",_i);                             \
        if ((INDENT) < 0) {                                \
            CcDebugTraceIndent += (INDENT);                \
        }                                                  \
        if (CcDebugTraceIndent < 0) {                      \
            CcDebugTraceIndent = 0;                        \
        }                                                  \
        for (_i=0; _i<CcDebugTraceIndent; _i+=1) {         \
            DbgPrint(" ");                                 \
        }                                                  \
        DbgPrint(X,Y,Z);                                   \
        if ((INDENT) > 0) {                                \
            CcDebugTraceIndent += (INDENT);                \
        }                                                  \
    }                                                      \
}

#define DebugDump(STR,LEVEL,PTR) {                         \
    LONG _i;                                               \
    VOID CcDump();                                         \
    if (((LEVEL) == 0) || (CcDebugTraceLevel & (LEVEL))) { \
        _i = (ULONG)PsGetCurrentThread();                  \
        DbgPrint("%08lx:",_i);                             \
        DbgPrint(STR);                                     \
        if (PTR != NULL) {CcDump(PTR);}                    \
        DbgBreakPoint();                                   \
    }                                                      \
}

#else //  ndef CCDBG_LOCK

extern KSPIN_LOCK CcDebugTraceLock;

#define DebugTrace(INDENT,LEVEL,X,Y) {                     \
    LONG _i;                                               \
    KIRQL _oldIrql;                                        \
    if (((LEVEL) == 0) || (CcDebugTraceLevel & (LEVEL))) { \
        _i = (ULONG)PsGetCurrentThread();                  \
        ExAcquireSpinLock( &CcDebugTraceLock, &_oldIrql ); \
        DbgPrint("%08lx:",_i);                             \
        if ((INDENT) < 0) {                                \
            CcDebugTraceIndent += (INDENT);                \
        }                                                  \
        if (CcDebugTraceIndent < 0) {                      \
            CcDebugTraceIndent = 0;                        \
        }                                                  \
        for (_i=0; _i<CcDebugTraceIndent; _i+=1) {         \
            DbgPrint(" ");                                 \
        }                                                  \
        DbgPrint(X,Y);                                     \
        if ((INDENT) > 0) {                                \
            CcDebugTraceIndent += (INDENT);                \
        }                                                  \
        ExReleaseSpinLock( &CcDebugTraceLock, _oldIrql );  \
    }                                                      \
}

#define DebugTrace2(INDENT,LEVEL,X,Y,Z) {                  \
    LONG _i;                                               \
    KIRQL _oldIrql;                                        \
    if (((LEVEL) == 0) || (CcDebugTraceLevel & (LEVEL))) { \
        _i = (ULONG)PsGetCurrentThread();                  \
        ExAcquireSpinLock( &CcDebugTraceLock, &_oldIrql ); \
        DbgPrint("%08lx:",_i);                             \
        if ((INDENT) < 0) {                                \
            CcDebugTraceIndent += (INDENT);                \
        }                                                  \
        if (CcDebugTraceIndent < 0) {                      \
            CcDebugTraceIndent = 0;                        \
        }                                                  \
        for (_i=0; _i<CcDebugTraceIndent; _i+=1) {         \
            DbgPrint(" ");                                 \
        }                                                  \
        DbgPrint(X,Y,Z);                                   \
        if ((INDENT) > 0) {                                \
            CcDebugTraceIndent += (INDENT);                \
        }                                                  \
      ExReleaseSpinLock( &CcDebugTraceLock, _oldIrql );  \
    }                                                      \
}

#define DebugDump(STR,LEVEL,PTR) {                         \
    LONG _i;                                               \
    KIRQL _oldIrql;                                        \
    VOID CcDump();                                         \
    if (((LEVEL) == 0) || (CcDebugTraceLevel & (LEVEL))) { \
        _i = (ULONG)PsGetCurrentThread();                  \
      ExAcquireSpinLock( &CcDebugTraceLock, &_oldIrql ); \
        DbgPrint("%08lx:",_i);                             \
        DbgPrint(STR);                                     \
        if (PTR != NULL) {CcDump(PTR);}                    \
        DbgBreakPoint();                                   \
      ExReleaseSpinLock( &CcDebugTraceLock, _oldIrql );  \
    }                                                      \
}

#endif //  else ndef CCDBG_LOCK

#else

#undef CCDBG_LOCK

#define DebugTrace(INDENT,LEVEL,X,Y) {NOTHING;}

#define DebugTrace2(INDENT,LEVEL,X,Y,Z) {NOTHING;}

#define DebugDump(STR,LEVEL,PTR) {NOTHING;}

#endif //  CCDBG

//
//  Global list of pinned Bcbs which may be examined for debug purposes
//

#if DBG

extern ULONG CcBcbCount;
extern LIST_ENTRY CcBcbList;

#endif

#endif  //  _CCh_
