mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2173 lines
57 KiB
2173 lines
57 KiB
/*++
|
|
|
|
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_
|