/*++ 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 #include #ifdef MEMPRINT #include #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 // // 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 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 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 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 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_