mirror of
https://github.com/reactos/reactos.git
synced 2025-08-06 11:53:16 +00:00
[NTOS]: Implement pool tag tracking and pool tag querying. Big Pool tags are not yet supported, but will be shortly. Expansion is not yet supported.
svn path=/trunk/; revision=55872
This commit is contained in:
parent
fb942b740a
commit
ec4df1d177
4 changed files with 548 additions and 10 deletions
|
@ -1239,9 +1239,8 @@ SSI_DEF(SystemFileCacheInformation)
|
||||||
/* Class 22 - Pool Tag Information */
|
/* Class 22 - Pool Tag Information */
|
||||||
QSI_DEF(SystemPoolTagInformation)
|
QSI_DEF(SystemPoolTagInformation)
|
||||||
{
|
{
|
||||||
/* FIXME */
|
if (Size < sizeof(SYSTEM_POOLTAG_INFORMATION)) return STATUS_INFO_LENGTH_MISMATCH;
|
||||||
DPRINT1("NtQuerySystemInformation - SystemPoolTagInformation not implemented\n");
|
return ExGetPoolTagInfo(Buffer, Size, ReqSize);
|
||||||
return STATUS_NOT_IMPLEMENTED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Class 23 - Interrupt Information for all processors */
|
/* Class 23 - Interrupt Information for all processors */
|
||||||
|
|
|
@ -130,6 +130,14 @@ typedef struct _HARDERROR_USER_PARAMETERS
|
||||||
#define ExpChangePushlock(x, y, z) InterlockedCompareExchangePointer((PVOID*)x, (PVOID)y, (PVOID)z)
|
#define ExpChangePushlock(x, y, z) InterlockedCompareExchangePointer((PVOID*)x, (PVOID)y, (PVOID)z)
|
||||||
#define ExpSetRundown(x, y) InterlockedExchangePointer(&x->Ptr, (PVOID)y)
|
#define ExpSetRundown(x, y) InterlockedExchangePointer(&x->Ptr, (PVOID)y)
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
NTAPI
|
||||||
|
ExGetPoolTagInfo(
|
||||||
|
IN PSYSTEM_POOLTAG_INFORMATION SystemInformation,
|
||||||
|
IN ULONG SystemInformationLength,
|
||||||
|
IN OUT PULONG ReturnLength OPTIONAL
|
||||||
|
);
|
||||||
|
|
||||||
/* INITIALIZATION FUNCTIONS *************************************************/
|
/* INITIALIZATION FUNCTIONS *************************************************/
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
|
|
|
@ -20,6 +20,14 @@
|
||||||
|
|
||||||
/* GLOBALS ********************************************************************/
|
/* GLOBALS ********************************************************************/
|
||||||
|
|
||||||
|
typedef struct _POOL_DPC_CONTEXT
|
||||||
|
{
|
||||||
|
PPOOL_TRACKER_TABLE PoolTrackTable;
|
||||||
|
SIZE_T PoolTrackTableSize;
|
||||||
|
PPOOL_TRACKER_TABLE PoolTrackTableExpansion;
|
||||||
|
SIZE_T PoolTrackTableSizeExpansion;
|
||||||
|
} POOL_DPC_CONTEXT, *PPOOL_DPC_CONTEXT;
|
||||||
|
|
||||||
ULONG ExpNumberOfPagedPools;
|
ULONG ExpNumberOfPagedPools;
|
||||||
POOL_DESCRIPTOR NonPagedPoolDescriptor;
|
POOL_DESCRIPTOR NonPagedPoolDescriptor;
|
||||||
PPOOL_DESCRIPTOR ExpPagedPoolDescriptor[16 + 1];
|
PPOOL_DESCRIPTOR ExpPagedPoolDescriptor[16 + 1];
|
||||||
|
@ -27,8 +35,11 @@ PPOOL_DESCRIPTOR PoolVector[2];
|
||||||
PKGUARDED_MUTEX ExpPagedPoolMutex;
|
PKGUARDED_MUTEX ExpPagedPoolMutex;
|
||||||
SIZE_T PoolTrackTableSize, PoolTrackTableMask;
|
SIZE_T PoolTrackTableSize, PoolTrackTableMask;
|
||||||
SIZE_T PoolBigPageTableSize, PoolBigPageTableHash;
|
SIZE_T PoolBigPageTableSize, PoolBigPageTableHash;
|
||||||
PPOOL_TRACKER_TABLE PoolTrackTable, PoolBigPageTable;
|
PPOOL_TRACKER_TABLE PoolTrackTable;
|
||||||
|
PPOOL_TRACKER_BIG_PAGES PoolBigPageTable;
|
||||||
KSPIN_LOCK ExpTaggedPoolLock;
|
KSPIN_LOCK ExpTaggedPoolLock;
|
||||||
|
ULONG PoolHitTag;
|
||||||
|
BOOLEAN ExStopBadTags;
|
||||||
|
|
||||||
/* Pool block/header/list access macros */
|
/* Pool block/header/list access macros */
|
||||||
#define POOL_ENTRY(x) (PPOOL_HEADER)((ULONG_PTR)(x) - sizeof(POOL_HEADER))
|
#define POOL_ENTRY(x) (PPOOL_HEADER)((ULONG_PTR)(x) - sizeof(POOL_HEADER))
|
||||||
|
@ -286,8 +297,345 @@ ExpCheckPoolBlocks(IN PVOID Block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FORCEINLINE
|
||||||
|
ULONG
|
||||||
|
ExpComputeHashForTag(IN ULONG Tag,
|
||||||
|
IN SIZE_T BucketMask)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Compute the hash by multiplying with a large prime number and then XORing
|
||||||
|
// with the HIDWORD of the result.
|
||||||
|
//
|
||||||
|
// Finally, AND with the bucket mask to generate a valid index/bucket into
|
||||||
|
// the table
|
||||||
|
//
|
||||||
|
ULONGLONG Result = 40543 * Tag;
|
||||||
|
return BucketMask & (Result ^ (Result >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
/* PRIVATE FUNCTIONS **********************************************************/
|
/* PRIVATE FUNCTIONS **********************************************************/
|
||||||
|
|
||||||
|
VOID
|
||||||
|
NTAPI
|
||||||
|
INIT_FUNCTION
|
||||||
|
ExpSeedHotTags(VOID)
|
||||||
|
{
|
||||||
|
ULONG i, Key, Hash, Index;
|
||||||
|
PPOOL_TRACKER_TABLE TrackTable = PoolTrackTable;
|
||||||
|
ULONG TagList[] =
|
||||||
|
{
|
||||||
|
' oI',
|
||||||
|
' laH',
|
||||||
|
'PldM',
|
||||||
|
'LooP',
|
||||||
|
'tSbO',
|
||||||
|
' prI',
|
||||||
|
'bdDN',
|
||||||
|
'LprI',
|
||||||
|
'pOoI',
|
||||||
|
' ldM',
|
||||||
|
'eliF',
|
||||||
|
'aVMC',
|
||||||
|
'dSeS',
|
||||||
|
'CFtN',
|
||||||
|
'looP',
|
||||||
|
'rPCT',
|
||||||
|
'bNMC',
|
||||||
|
'dTeS',
|
||||||
|
'sFtN',
|
||||||
|
'TPCT',
|
||||||
|
'CPCT',
|
||||||
|
' yeK',
|
||||||
|
'qSbO',
|
||||||
|
'mNoI',
|
||||||
|
'aEoI',
|
||||||
|
'cPCT',
|
||||||
|
'aFtN',
|
||||||
|
'0ftN',
|
||||||
|
'tceS',
|
||||||
|
'SprI',
|
||||||
|
'ekoT',
|
||||||
|
' eS',
|
||||||
|
'lCbO',
|
||||||
|
'cScC',
|
||||||
|
'lFtN',
|
||||||
|
'cAeS',
|
||||||
|
'mfSF',
|
||||||
|
'kWcC',
|
||||||
|
'miSF',
|
||||||
|
'CdfA',
|
||||||
|
'EdfA',
|
||||||
|
'orSF',
|
||||||
|
'nftN',
|
||||||
|
'PRIU',
|
||||||
|
'rFpN',
|
||||||
|
'RFpN',
|
||||||
|
'aPeS',
|
||||||
|
'sUeS',
|
||||||
|
'FpcA',
|
||||||
|
'MpcA',
|
||||||
|
'cSeS',
|
||||||
|
'mNbO',
|
||||||
|
'sFpN',
|
||||||
|
'uLeS',
|
||||||
|
'DPcS',
|
||||||
|
'nevE',
|
||||||
|
'vrqR',
|
||||||
|
'ldaV',
|
||||||
|
' pP',
|
||||||
|
'SdaV',
|
||||||
|
' daV',
|
||||||
|
'LdaV',
|
||||||
|
'FdaV',
|
||||||
|
' GIB',
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Loop all 64 hot tags
|
||||||
|
//
|
||||||
|
ASSERT((sizeof(TagList) / sizeof(ULONG)) == 64);
|
||||||
|
for (i = 0; i < sizeof(TagList) / sizeof(ULONG); i++)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Get the current tag, and compute its hash in the tracker table
|
||||||
|
//
|
||||||
|
Key = TagList[i];
|
||||||
|
Hash = ExpComputeHashForTag(Key, PoolTrackTableMask);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Loop all the hashes in this index/bucket
|
||||||
|
//
|
||||||
|
Index = Hash;
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Find an empty entry, and make sure this isn't the last hash that
|
||||||
|
// can fit.
|
||||||
|
//
|
||||||
|
// On checked builds, also make sure this is the first time we are
|
||||||
|
// seeding this tag.
|
||||||
|
//
|
||||||
|
ASSERT(TrackTable[Hash].Key != Key);
|
||||||
|
if (!(TrackTable[Hash].Key) && (Hash != PoolTrackTableSize - 1))
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// It has been seeded, move on to the next tag
|
||||||
|
//
|
||||||
|
TrackTable[Hash].Key = Key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// This entry was already taken, compute the next possible hash while
|
||||||
|
// making sure we're not back at our initial index.
|
||||||
|
//
|
||||||
|
ASSERT(TrackTable[Hash].Key != Key);
|
||||||
|
Hash = (Hash + 1) & PoolTrackTableMask;
|
||||||
|
if (Hash == Index) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID
|
||||||
|
NTAPI
|
||||||
|
ExpRemovePoolTracker(IN ULONG Key,
|
||||||
|
IN SIZE_T NumberOfBytes,
|
||||||
|
IN POOL_TYPE PoolType)
|
||||||
|
{
|
||||||
|
ULONG Hash, Index;
|
||||||
|
PPOOL_TRACKER_TABLE Table, TableEntry;
|
||||||
|
SIZE_T TableMask, TableSize;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Remove the PROTECTED_POOL flag which is not part of the tag
|
||||||
|
//
|
||||||
|
Key &= ~PROTECTED_POOL;
|
||||||
|
|
||||||
|
//
|
||||||
|
// With WinDBG you can set a tag you want to break on when an allocation is
|
||||||
|
// attempted
|
||||||
|
//
|
||||||
|
if (Key == PoolHitTag) DbgBreakPoint();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Why the double indirection? Because normally this function is also used
|
||||||
|
// when doing session pool allocations, which has another set of tables,
|
||||||
|
// sizes, and masks that live in session pool. Now we don't support session
|
||||||
|
// pool so we only ever use the regular tables, but I'm keeping the code this
|
||||||
|
// way so that the day we DO support session pool, it won't require that
|
||||||
|
// many changes
|
||||||
|
//
|
||||||
|
Table = PoolTrackTable;
|
||||||
|
TableMask = PoolTrackTableMask;
|
||||||
|
TableSize = PoolTrackTableSize;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Compute the hash for this key, and loop all the possible buckets
|
||||||
|
//
|
||||||
|
Hash = ExpComputeHashForTag(Key, TableMask);
|
||||||
|
Index = Hash;
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Have we found the entry for this tag? */
|
||||||
|
//
|
||||||
|
TableEntry = &Table[Hash];
|
||||||
|
if (TableEntry->Key == Key)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Decrement the counters depending on if this was paged or nonpaged
|
||||||
|
// pool
|
||||||
|
//
|
||||||
|
if ((PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool)
|
||||||
|
{
|
||||||
|
InterlockedIncrement(&TableEntry->NonPagedFrees);
|
||||||
|
InterlockedExchangeAddSizeT(&TableEntry->NonPagedBytes, -NumberOfBytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
InterlockedIncrement(&TableEntry->PagedFrees);
|
||||||
|
InterlockedExchangeAddSizeT(&TableEntry->PagedBytes, -NumberOfBytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// We should have only ended up with an empty entry if we've reached
|
||||||
|
// the last bucket
|
||||||
|
//
|
||||||
|
if (!TableEntry->Key) ASSERT(Hash == TableMask);
|
||||||
|
|
||||||
|
//
|
||||||
|
// This path is hit when we don't have an entry, and the current bucket
|
||||||
|
// is full, so we simply try the next one
|
||||||
|
//
|
||||||
|
Hash = (Hash + 1) & TableMask;
|
||||||
|
if (Hash == Index) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// And finally this path is hit when all the buckets are full, and we need
|
||||||
|
// some expansion. This path is not yet supported in ReactOS and so we'll
|
||||||
|
// ignore the tag
|
||||||
|
//
|
||||||
|
DPRINT1("Out of pool tag space, ignoring...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID
|
||||||
|
NTAPI
|
||||||
|
ExpInsertPoolTracker(IN ULONG Key,
|
||||||
|
IN SIZE_T NumberOfBytes,
|
||||||
|
IN POOL_TYPE PoolType)
|
||||||
|
{
|
||||||
|
ULONG Hash, Index;
|
||||||
|
KIRQL OldIrql;
|
||||||
|
PPOOL_TRACKER_TABLE Table, TableEntry;
|
||||||
|
SIZE_T TableMask, TableSize;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Remove the PROTECTED_POOL flag which is not part of the tag
|
||||||
|
//
|
||||||
|
Key &= ~PROTECTED_POOL;
|
||||||
|
|
||||||
|
//
|
||||||
|
// With WinDBG you can set a tag you want to break on when an allocation is
|
||||||
|
// attempted
|
||||||
|
//
|
||||||
|
if (Key == PoolHitTag) DbgBreakPoint();
|
||||||
|
|
||||||
|
//
|
||||||
|
// There is also an internal flag you can set to break on malformed tags
|
||||||
|
//
|
||||||
|
if (ExStopBadTags) ASSERT(Key & 0xFFFFFF00);
|
||||||
|
|
||||||
|
//
|
||||||
|
// ASSERT on ReactOS features not yet supported
|
||||||
|
//
|
||||||
|
ASSERT(!(PoolType & SESSION_POOL_MASK));
|
||||||
|
ASSERT(KeGetCurrentProcessorNumber() == 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Why the double indirection? Because normally this function is also used
|
||||||
|
// when doing session pool allocations, which has another set of tables,
|
||||||
|
// sizes, and masks that live in session pool. Now we don't support session
|
||||||
|
// pool so we only ever use the regular tables, but I'm keeping the code this
|
||||||
|
// way so that the day we DO support session pool, it won't require that
|
||||||
|
// many changes
|
||||||
|
//
|
||||||
|
Table = PoolTrackTable;
|
||||||
|
TableMask = PoolTrackTableMask;
|
||||||
|
TableSize = PoolTrackTableSize;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Compute the hash for this key, and loop all the possible buckets
|
||||||
|
//
|
||||||
|
Hash = ExpComputeHashForTag(Key, TableMask);
|
||||||
|
Index = Hash;
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Do we already have an entry for this tag? */
|
||||||
|
//
|
||||||
|
TableEntry = &Table[Hash];
|
||||||
|
if (TableEntry->Key == Key)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Increment the counters depending on if this was paged or nonpaged
|
||||||
|
// pool
|
||||||
|
//
|
||||||
|
if ((PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool)
|
||||||
|
{
|
||||||
|
InterlockedIncrement(&TableEntry->NonPagedAllocs);
|
||||||
|
InterlockedExchangeAddSizeT(&TableEntry->NonPagedBytes, NumberOfBytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
InterlockedIncrement(&TableEntry->PagedAllocs);
|
||||||
|
InterlockedExchangeAddSizeT(&TableEntry->PagedBytes, NumberOfBytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// We don't have an entry yet, but we've found a free bucket for it
|
||||||
|
//
|
||||||
|
if (!(TableEntry->Key) && (Hash != PoolTrackTableSize - 1))
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// We need to hold the lock while creating a new entry, since other
|
||||||
|
// processors might be in this code path as well
|
||||||
|
//
|
||||||
|
ExAcquireSpinLock(&ExpTaggedPoolLock, &OldIrql);
|
||||||
|
if (!PoolTrackTable[Hash].Key)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// We've won the race, so now create this entry in the bucket
|
||||||
|
//
|
||||||
|
ASSERT(Table[Hash].Key == 0);
|
||||||
|
PoolTrackTable[Hash].Key = Key;
|
||||||
|
TableEntry->Key = Key;
|
||||||
|
}
|
||||||
|
ExReleaseSpinLock(&ExpTaggedPoolLock, OldIrql);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Now we force the loop to run again, and we should now end up in
|
||||||
|
// the code path above which does the interlocked increments...
|
||||||
|
//
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// This path is hit when we don't have an entry, and the current bucket
|
||||||
|
// is full, so we simply try the next one
|
||||||
|
//
|
||||||
|
Hash = (Hash + 1) & TableMask;
|
||||||
|
if (Hash == Index) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// And finally this path is hit when all the buckets are full, and we need
|
||||||
|
// some expansion. This path is not yet supported in ReactOS and so we'll
|
||||||
|
// ignore the tag
|
||||||
|
//
|
||||||
|
DPRINT1("Out of pool tag space, ignoring...\n");
|
||||||
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
NTAPI
|
NTAPI
|
||||||
INIT_FUNCTION
|
INIT_FUNCTION
|
||||||
|
@ -499,6 +847,7 @@ InitializePool(IN POOL_TYPE PoolType,
|
||||||
PoolBigPageTableHash = PoolBigPageTableSize - 1;
|
PoolBigPageTableHash = PoolBigPageTableSize - 1;
|
||||||
RtlZeroMemory(PoolBigPageTable,
|
RtlZeroMemory(PoolBigPageTable,
|
||||||
PoolBigPageTableSize * sizeof(POOL_TRACKER_BIG_PAGES));
|
PoolBigPageTableSize * sizeof(POOL_TRACKER_BIG_PAGES));
|
||||||
|
for (i = 0; i < PoolBigPageTableSize; i++) PoolBigPageTable[i].Va = (PVOID)1;
|
||||||
|
|
||||||
//
|
//
|
||||||
// During development, print this out so we can see what's happening
|
// During development, print this out so we can see what's happening
|
||||||
|
@ -508,6 +857,14 @@ InitializePool(IN POOL_TYPE PoolType,
|
||||||
DPRINT1("EXPOOL: Big Pool Tracker Table at: 0x%p with 0x%lx bytes\n",
|
DPRINT1("EXPOOL: Big Pool Tracker Table at: 0x%p with 0x%lx bytes\n",
|
||||||
PoolBigPageTable, PoolBigPageTableSize * sizeof(POOL_TRACKER_BIG_PAGES));
|
PoolBigPageTable, PoolBigPageTableSize * sizeof(POOL_TRACKER_BIG_PAGES));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Insert the generic tracker for all of big pool
|
||||||
|
//
|
||||||
|
ExpInsertPoolTracker('looP',
|
||||||
|
ROUND_TO_PAGES(PoolBigPageTableSize *
|
||||||
|
sizeof(POOL_TRACKER_BIG_PAGES)),
|
||||||
|
NonPagedPool);
|
||||||
|
|
||||||
//
|
//
|
||||||
// No support for NUMA systems at this time
|
// No support for NUMA systems at this time
|
||||||
//
|
//
|
||||||
|
@ -565,6 +922,13 @@ InitializePool(IN POOL_TYPE PoolType,
|
||||||
0,
|
0,
|
||||||
Threshold,
|
Threshold,
|
||||||
ExpPagedPoolMutex);
|
ExpPagedPoolMutex);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Insert the generic tracker for all of nonpaged pool
|
||||||
|
//
|
||||||
|
ExpInsertPoolTracker('looP',
|
||||||
|
ROUND_TO_PAGES(PoolTrackTableSize * sizeof(POOL_TRACKER_TABLE)),
|
||||||
|
NonPagedPool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,6 +980,146 @@ ExUnlockPool(IN PPOOL_DESCRIPTOR Descriptor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOID
|
||||||
|
NTAPI
|
||||||
|
ExpGetPoolTagInfoTarget(IN PKDPC Dpc,
|
||||||
|
IN PVOID DeferredContext,
|
||||||
|
IN PVOID SystemArgument1,
|
||||||
|
IN PVOID SystemArgument2)
|
||||||
|
{
|
||||||
|
PPOOL_DPC_CONTEXT Context = DeferredContext;
|
||||||
|
UNREFERENCED_PARAMETER(Dpc);
|
||||||
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Make sure we win the race, and if we did, copy the data atomically
|
||||||
|
//
|
||||||
|
if (KeSignalCallDpcSynchronize(SystemArgument2))
|
||||||
|
{
|
||||||
|
RtlCopyMemory(Context->PoolTrackTable,
|
||||||
|
PoolTrackTable,
|
||||||
|
Context->PoolTrackTableSize * sizeof(POOL_TRACKER_TABLE));
|
||||||
|
|
||||||
|
//
|
||||||
|
// This is here because ReactOS does not yet support expansion
|
||||||
|
//
|
||||||
|
ASSERT(Context->PoolTrackTableSizeExpansion == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Regardless of whether we won or not, we must now synchronize and then
|
||||||
|
// decrement the barrier since this is one more processor that has completed
|
||||||
|
// the callback.
|
||||||
|
//
|
||||||
|
KeSignalCallDpcSynchronize(SystemArgument2);
|
||||||
|
KeSignalCallDpcDone(SystemArgument1);
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
NTAPI
|
||||||
|
ExGetPoolTagInfo(IN PSYSTEM_POOLTAG_INFORMATION SystemInformation,
|
||||||
|
IN ULONG SystemInformationLength,
|
||||||
|
IN OUT PULONG ReturnLength OPTIONAL)
|
||||||
|
{
|
||||||
|
SIZE_T TableSize, CurrentLength;
|
||||||
|
ULONG EntryCount;
|
||||||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||||||
|
PSYSTEM_POOLTAG TagEntry;
|
||||||
|
PPOOL_TRACKER_TABLE Buffer, TrackerEntry;
|
||||||
|
POOL_DPC_CONTEXT Context;
|
||||||
|
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Keep track of how much data the caller's buffer must hold
|
||||||
|
//
|
||||||
|
CurrentLength = FIELD_OFFSET(SYSTEM_POOLTAG_INFORMATION, TagInfo);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize the caller's buffer
|
||||||
|
//
|
||||||
|
TagEntry = &SystemInformation->TagInfo[0];
|
||||||
|
SystemInformation->Count = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Capture the number of entries, and the total size needed to make a copy
|
||||||
|
// of the table
|
||||||
|
//
|
||||||
|
EntryCount = PoolTrackTableSize;
|
||||||
|
TableSize = EntryCount * sizeof(POOL_TRACKER_TABLE);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Allocate the "Generic DPC" temporary buffer
|
||||||
|
//
|
||||||
|
Buffer = ExAllocatePoolWithTag(NonPagedPool, TableSize, 'ofnI');
|
||||||
|
if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Do a "Generic DPC" to atomically retrieve the tag and allocation data
|
||||||
|
//
|
||||||
|
Context.PoolTrackTable = Buffer;
|
||||||
|
Context.PoolTrackTableSize = PoolTrackTableSize;
|
||||||
|
Context.PoolTrackTableExpansion = NULL;
|
||||||
|
Context.PoolTrackTableSizeExpansion = 0;
|
||||||
|
KeGenericCallDpc(ExpGetPoolTagInfoTarget, &Context);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Now parse the results
|
||||||
|
//
|
||||||
|
for (TrackerEntry = Buffer; TrackerEntry < (Buffer + EntryCount); TrackerEntry++)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// If the entry is empty, skip it
|
||||||
|
//
|
||||||
|
if (!TrackerEntry->Key) continue;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Otherwise, add one more entry to the caller's buffer, and ensure that
|
||||||
|
// enough space has been allocated in it
|
||||||
|
//
|
||||||
|
SystemInformation->Count++;
|
||||||
|
CurrentLength += sizeof(*TagEntry);
|
||||||
|
if (SystemInformationLength < CurrentLength)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// The caller's buffer is too small, so set a failure code. The
|
||||||
|
// caller will know the count, as well as how much space is needed.
|
||||||
|
//
|
||||||
|
// We do NOT break out of the loop, because we want to keep incrementing
|
||||||
|
// the Count as well as CurrentLength so that the caller can know the
|
||||||
|
// final numbers
|
||||||
|
//
|
||||||
|
Status = STATUS_INFO_LENGTH_MISMATCH;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Small sanity check that our accounting is working correctly
|
||||||
|
//
|
||||||
|
ASSERT(TrackerEntry->PagedAllocs >= TrackerEntry->PagedFrees);
|
||||||
|
ASSERT(TrackerEntry->NonPagedAllocs >= TrackerEntry->NonPagedFrees);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the data into the caller's buffer
|
||||||
|
//
|
||||||
|
TagEntry->TagUlong = TrackerEntry->Key;
|
||||||
|
TagEntry->PagedAllocs = TrackerEntry->PagedAllocs;
|
||||||
|
TagEntry->PagedFrees = TrackerEntry->PagedFrees;
|
||||||
|
TagEntry->PagedUsed = TrackerEntry->PagedBytes;
|
||||||
|
TagEntry->NonPagedAllocs = TrackerEntry->NonPagedAllocs;
|
||||||
|
TagEntry->NonPagedFrees = TrackerEntry->NonPagedFrees;
|
||||||
|
TagEntry->NonPagedUsed = TrackerEntry->NonPagedBytes;
|
||||||
|
TagEntry++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Free the "Generic DPC" temporary buffer, return the buffer length and status
|
||||||
|
//
|
||||||
|
ExFreePool(Buffer);
|
||||||
|
if (ReturnLength) *ReturnLength = CurrentLength;
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
/* PUBLIC FUNCTIONS ***********************************************************/
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -851,6 +1355,13 @@ ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
|
||||||
ExpCheckPoolBlocks(Entry);
|
ExpCheckPoolBlocks(Entry);
|
||||||
ExUnlockPool(PoolDesc, OldIrql);
|
ExUnlockPool(PoolDesc, OldIrql);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Track this allocation
|
||||||
|
//
|
||||||
|
ExpInsertPoolTracker(Tag,
|
||||||
|
Entry->BlockSize * POOL_BLOCK_SIZE,
|
||||||
|
PoolType);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Return the pool allocation
|
// Return the pool allocation
|
||||||
//
|
//
|
||||||
|
@ -865,8 +1376,7 @@ ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
|
||||||
// There were no free entries left, so we have to allocate a new fresh page
|
// There were no free entries left, so we have to allocate a new fresh page
|
||||||
//
|
//
|
||||||
Entry = MiAllocatePoolPages(PoolType, PAGE_SIZE);
|
Entry = MiAllocatePoolPages(PoolType, PAGE_SIZE);
|
||||||
if (Entry == NULL)
|
if (Entry == NULL) return NULL;
|
||||||
return NULL;
|
|
||||||
|
|
||||||
Entry->Ulong1 = 0;
|
Entry->Ulong1 = 0;
|
||||||
Entry->BlockSize = i;
|
Entry->BlockSize = i;
|
||||||
|
@ -911,6 +1421,13 @@ ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
|
||||||
ExUnlockPool(PoolDesc, OldIrql);
|
ExUnlockPool(PoolDesc, OldIrql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Track this allocation
|
||||||
|
//
|
||||||
|
ExpInsertPoolTracker(Tag,
|
||||||
|
Entry->BlockSize * POOL_BLOCK_SIZE,
|
||||||
|
PoolType);
|
||||||
|
|
||||||
//
|
//
|
||||||
// And return the pool allocation
|
// And return the pool allocation
|
||||||
//
|
//
|
||||||
|
@ -946,6 +1463,7 @@ ExFreePoolWithTag(IN PVOID P,
|
||||||
KIRQL OldIrql;
|
KIRQL OldIrql;
|
||||||
POOL_TYPE PoolType;
|
POOL_TYPE PoolType;
|
||||||
PPOOL_DESCRIPTOR PoolDesc;
|
PPOOL_DESCRIPTOR PoolDesc;
|
||||||
|
ULONG Tag;
|
||||||
BOOLEAN Combined = FALSE;
|
BOOLEAN Combined = FALSE;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -984,6 +1502,19 @@ ExFreePoolWithTag(IN PVOID P,
|
||||||
PoolType = (Entry->PoolType - 1) & BASE_POOL_TYPE_MASK;
|
PoolType = (Entry->PoolType - 1) & BASE_POOL_TYPE_MASK;
|
||||||
PoolDesc = PoolVector[PoolType];
|
PoolDesc = PoolVector[PoolType];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get the pool tag and get rid of the PROTECTED_POOL flag
|
||||||
|
//
|
||||||
|
Tag = Entry->PoolTag;
|
||||||
|
if (Tag & PROTECTED_POOL) Tag &= ~PROTECTED_POOL;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Stop tracking this allocation
|
||||||
|
//
|
||||||
|
ExpRemovePoolTracker(Tag,
|
||||||
|
BlockSize * POOL_BLOCK_SIZE,
|
||||||
|
Entry->PoolType - 1);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Get the pointer to the next entry
|
// Get the pointer to the next entry
|
||||||
//
|
//
|
||||||
|
|
|
@ -348,11 +348,11 @@ C_ASSERT(POOL_BLOCK_SIZE == sizeof(LIST_ENTRY));
|
||||||
typedef struct _POOL_TRACKER_TABLE
|
typedef struct _POOL_TRACKER_TABLE
|
||||||
{
|
{
|
||||||
ULONG Key;
|
ULONG Key;
|
||||||
ULONG NonPagedAllocs;
|
LONG NonPagedAllocs;
|
||||||
ULONG NonPagedFrees;
|
LONG NonPagedFrees;
|
||||||
SIZE_T NonPagedBytes;
|
SIZE_T NonPagedBytes;
|
||||||
ULONG PagedAllocs;
|
LONG PagedAllocs;
|
||||||
ULONG PagedFrees;
|
LONG PagedFrees;
|
||||||
SIZE_T PagedBytes;
|
SIZE_T PagedBytes;
|
||||||
} POOL_TRACKER_TABLE, *PPOOL_TRACKER_TABLE;
|
} POOL_TRACKER_TABLE, *PPOOL_TRACKER_TABLE;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue