PKGUARDED_MUTEX ExpPagedPoolMutex;
SIZE_T PoolTrackTableSize, PoolTrackTableMask;
SIZE_T PoolBigPageTableSize, PoolBigPageTableHash;
+ULONG ExpBigTableExpansionFailed;
PPOOL_TRACKER_TABLE PoolTrackTable;
PPOOL_TRACKER_BIG_PAGES PoolBigPageTable;
KSPIN_LOCK ExpTaggedPoolLock;
ULONG ExpPoolBigEntriesInUse;
ULONG ExpPoolFlags;
ULONG ExPoolFailures;
+ULONGLONG MiLastPoolDumpTime;
/* Pool block/header/list access macros */
#define POOL_ENTRY(x) (PPOOL_HEADER)((ULONG_PTR)(x) - sizeof(POOL_HEADER))
}
#if DBG
-FORCEINLINE
-BOOLEAN
-ExpTagAllowPrint(CHAR Tag)
-{
- if ((Tag >= 'a' && Tag <= 'z') ||
- (Tag >= 'A' && Tag <= 'Z') ||
- Tag == ' ')
- {
- return TRUE;
- }
-
- return FALSE;
-}
+/*
+ * FORCEINLINE
+ * BOOLEAN
+ * ExpTagAllowPrint(CHAR Tag);
+ */
+#define ExpTagAllowPrint(Tag) \
+ ((Tag) >= 0x20 /* Space */ && (Tag) <= 0x7E /* Tilde */)
#ifdef KDBG
#define MiDumperPrint(dbg, fmt, ...) \
{
if (Verbose)
{
- MiDumperPrint(CalledFromDbg, "%x\t%ld\t\t%ld\t\t%ld\t\t%ld\t\t%ld\t\t%ld\t\t%ld\t\t%ld\n", TableEntry->Key,
+ MiDumperPrint(CalledFromDbg, "0x%08x\t%ld\t\t%ld\t\t%ld\t\t%ld\t\t%ld\t\t%ld\t\t%ld\t\t%ld\n", TableEntry->Key,
TableEntry->NonPagedAllocs, TableEntry->NonPagedFrees,
(TableEntry->NonPagedAllocs - TableEntry->NonPagedFrees), TableEntry->NonPagedBytes,
TableEntry->PagedAllocs, TableEntry->PagedFrees,
}
else
{
- MiDumperPrint(CalledFromDbg, "%x\t%ld\t\t%ld\t\t%ld\t\t%ld\n", TableEntry->Key,
+ MiDumperPrint(CalledFromDbg, "0x%08x\t%ld\t\t%ld\t\t%ld\t\t%ld\n", TableEntry->Key,
TableEntry->NonPagedAllocs, TableEntry->NonPagedBytes,
TableEntry->PagedAllocs, TableEntry->PagedBytes);
}
/* PRIVATE FUNCTIONS **********************************************************/
+INIT_FUNCTION
VOID
NTAPI
-INIT_SECTION
ExpSeedHotTags(VOID)
{
ULONG i, Key, Hash, Index;
DPRINT1("Out of pool tag space, ignoring...\n");
}
+INIT_FUNCTION
VOID
NTAPI
-INIT_SECTION
ExInitializePoolDescriptor(IN PPOOL_DESCRIPTOR PoolDescriptor,
IN POOL_TYPE PoolType,
IN ULONG PoolIndex,
ASSERT(PoolType != PagedPoolSession);
}
+INIT_FUNCTION
VOID
NTAPI
-INIT_SECTION
InitializePool(IN POOL_TYPE PoolType,
IN ULONG Threshold)
{
PoolBigPageTableHash = PoolBigPageTableSize - 1;
RtlZeroMemory(PoolBigPageTable,
PoolBigPageTableSize * sizeof(POOL_TRACKER_BIG_PAGES));
- for (i = 0; i < PoolBigPageTableSize; i++) PoolBigPageTable[i].Va = (PVOID)1;
+ for (i = 0; i < PoolBigPageTableSize; i++)
+ {
+ PoolBigPageTable[i].Va = (PVOID)POOL_BIG_TABLE_ENTRY_FREE;
+ }
//
// During development, print this out so we can see what's happening
return Status;
}
+_IRQL_requires_(DISPATCH_LEVEL)
+BOOLEAN
+NTAPI
+ExpExpandBigPageTable(
+ _In_ _IRQL_restores_ KIRQL OldIrql)
+{
+ ULONG OldSize = PoolBigPageTableSize;
+ ULONG NewSize = 2 * OldSize;
+ ULONG NewSizeInBytes;
+ PPOOL_TRACKER_BIG_PAGES NewTable;
+ PPOOL_TRACKER_BIG_PAGES OldTable;
+ ULONG i;
+ ULONG PagesFreed;
+ ULONG Hash;
+ ULONG HashMask;
+
+ /* Must be holding ExpLargePoolTableLock */
+ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+ /* Make sure we don't overflow */
+ if (!NT_SUCCESS(RtlULongMult(2,
+ OldSize * sizeof(POOL_TRACKER_BIG_PAGES),
+ &NewSizeInBytes)))
+ {
+ DPRINT1("Overflow expanding big page table. Size=%lu\n", OldSize);
+ KeReleaseSpinLock(&ExpLargePoolTableLock, OldIrql);
+ return FALSE;
+ }
+
+ NewTable = MiAllocatePoolPages(NonPagedPool, NewSizeInBytes);
+ if (NewTable == NULL)
+ {
+ DPRINT1("Could not allocate %lu bytes for new big page table\n", NewSizeInBytes);
+ KeReleaseSpinLock(&ExpLargePoolTableLock, OldIrql);
+ return FALSE;
+ }
+
+ DPRINT("Expanding big pool tracker table to %lu entries\n", NewSize);
+
+ /* Initialize the new table */
+ RtlZeroMemory(NewTable, NewSizeInBytes);
+ for (i = 0; i < NewSize; i++)
+ {
+ NewTable[i].Va = (PVOID)POOL_BIG_TABLE_ENTRY_FREE;
+ }
+
+ /* Copy over all items */
+ OldTable = PoolBigPageTable;
+ HashMask = NewSize - 1;
+ for (i = 0; i < OldSize; i++)
+ {
+ /* Skip over empty items */
+ if ((ULONG_PTR)OldTable[i].Va & POOL_BIG_TABLE_ENTRY_FREE)
+ {
+ continue;
+ }
+
+ /* Recalculate the hash due to the new table size */
+ Hash = ExpComputePartialHashForAddress(OldTable[i].Va) & HashMask;
+
+ /* Find the location in the new table */
+ while (!((ULONG_PTR)NewTable[Hash].Va & POOL_BIG_TABLE_ENTRY_FREE))
+ {
+ Hash = (Hash + 1) & HashMask;
+ }
+
+ /* We just enlarged the table, so we must have space */
+ ASSERT((ULONG_PTR)NewTable[Hash].Va & POOL_BIG_TABLE_ENTRY_FREE);
+
+ /* Finally, copy the item */
+ NewTable[Hash] = OldTable[i];
+ }
+
+ /* Activate the new table */
+ PoolBigPageTable = NewTable;
+ PoolBigPageTableSize = NewSize;
+ PoolBigPageTableHash = PoolBigPageTableSize - 1;
+
+ /* Release the lock, we're done changing global state */
+ KeReleaseSpinLock(&ExpLargePoolTableLock, OldIrql);
+
+ /* Free the old table and update our tracker */
+ PagesFreed = MiFreePoolPages(OldTable);
+ ExpRemovePoolTracker('looP', PagesFreed << PAGE_SHIFT, 0);
+ ExpInsertPoolTracker('looP', ALIGN_UP_BY(NewSizeInBytes, PAGE_SIZE), 0);
+
+ return TRUE;
+}
+
BOOLEAN
NTAPI
ExpAddTagForBigPages(IN PVOID Va,
//
// As the table is expandable, these values must only be read after acquiring
// the lock to avoid a teared access during an expansion
+ // NOTE: Windows uses a special reader/writer SpinLock to improve
+ // performance in the common case (add/remove a tracker entry)
//
+Retry:
Hash = ExpComputePartialHashForAddress(Va);
KeAcquireSpinLock(&ExpLargePoolTableLock, &OldIrql);
Hash &= PoolBigPageTableHash;
//
// Make sure that this is a free entry and attempt to atomically make the
// entry busy now
+ // NOTE: the Interlocked operation cannot fail with an exclusive SpinLock
//
OldVa = Entry->Va;
if (((ULONG_PTR)OldVa & POOL_BIG_TABLE_ENTRY_FREE) &&
- (InterlockedCompareExchangePointer(&Entry->Va, Va, OldVa) == OldVa))
+ (NT_VERIFY(InterlockedCompareExchangePointer(&Entry->Va, Va, OldVa) == OldVa)))
{
//
// We now own this entry, write down the size and the pool tag
InterlockedIncrementUL(&ExpPoolBigEntriesInUse);
if ((i >= 16) && (ExpPoolBigEntriesInUse > (TableSize / 4)))
{
- DPRINT("Should attempt expansion since we now have %lu entries\n",
+ DPRINT("Attempting expansion since we now have %lu entries\n",
ExpPoolBigEntriesInUse);
+ ASSERT(TableSize == PoolBigPageTableSize);
+ ExpExpandBigPageTable(OldIrql);
+ return TRUE;
}
//
} while (Entry != EntryStart);
//
- // This means there's no free hash buckets whatsoever, so we would now have
+ // This means there's no free hash buckets whatsoever, so we now have
// to attempt expanding the table
//
- DPRINT1("Big pool expansion needed, not implemented!\n");
- KeReleaseSpinLock(&ExpLargePoolTableLock, OldIrql);
+ ASSERT(TableSize == PoolBigPageTableSize);
+ if (ExpExpandBigPageTable(OldIrql))
+ {
+ goto Retry;
+ }
+ ExpBigTableExpansionFailed++;
+ DPRINT1("Big pool table expansion failed\n");
return FALSE;
}
#endif
//
- // FIXME: Not yet supported
+ // Get the amount of hits in the system lookaside lists
//
- *NonPagedPoolLookasideHits += 0;
- *PagedPoolLookasideHits += 0;
+ if (!IsListEmpty(&ExPoolLookasideListHead))
+ {
+ PLIST_ENTRY ListEntry;
+
+ for (ListEntry = ExPoolLookasideListHead.Flink;
+ ListEntry != &ExPoolLookasideListHead;
+ ListEntry = ListEntry->Flink)
+ {
+ PGENERAL_LOOKASIDE Lookaside;
+
+ Lookaside = CONTAINING_RECORD(ListEntry, GENERAL_LOOKASIDE, ListEntry);
+
+ if (Lookaside->Type == NonPagedPool)
+ {
+ *NonPagedPoolLookasideHits += Lookaside->AllocateHits;
+ }
+ else
+ {
+ *PagedPoolLookasideHits += Lookaside->AllocateHits;
+ }
+ }
+ }
}
VOID
#if DBG
//
// Out of memory, display current consumption
+ // Let's consider that if the caller wanted more
+ // than a hundred pages, that's a bogus caller
+ // and we are not out of memory. Dump at most
+ // once a second to avoid spamming the log.
//
- MiDumpPoolConsumers(FALSE, 0, 0, 0);
+ if (NumberOfBytes < 100 * PAGE_SIZE &&
+ KeQueryInterruptTime() >= MiLastPoolDumpTime + 10000000)
+ {
+ MiDumpPoolConsumers(FALSE, 0, 0, 0);
+ MiLastPoolDumpTime = KeQueryInterruptTime();
+ }
#endif
//
#if DBG
//
// Out of memory, display current consumption
+ // Let's consider that if the caller wanted more
+ // than a hundred pages, that's a bogus caller
+ // and we are not out of memory. Dump at most
+ // once a second to avoid spamming the log.
//
- MiDumpPoolConsumers(FALSE, 0, 0, 0);
+ if (NumberOfBytes < 100 * PAGE_SIZE &&
+ KeQueryInterruptTime() >= MiLastPoolDumpTime + 10000000)
+ {
+ MiDumpPoolConsumers(FALSE, 0, 0, 0);
+ MiLastPoolDumpTime = KeQueryInterruptTime();
+ }
#endif
//
if (TagToFree && TagToFree != Tag)
{
DPRINT1("Freeing pool - invalid tag specified: %.4s != %.4s\n", (char*)&TagToFree, (char*)&Tag);
+#if DBG
KeBugCheckEx(BAD_POOL_CALLER, 0x0A, (ULONG_PTR)P, Tag, TagToFree);
+#endif
}
//
if (TagToFree && TagToFree != Tag)
{
DPRINT1("Freeing pool - invalid tag specified: %.4s != %.4s\n", (char*)&TagToFree, (char*)&Tag);
+#if DBG
KeBugCheckEx(BAD_POOL_CALLER, 0x0A, (ULONG_PTR)P, Tag, TagToFree);
+#endif
}
//
return Buffer;
}
-#if DBG && defined(KDBG)
-
-BOOLEAN
-ExpKdbgExtPool(
- ULONG Argc,
- PCHAR Argv[])
-{
- ULONG_PTR Address = 0, Flags = 0;
- PVOID PoolPage;
- PPOOL_HEADER Entry;
- BOOLEAN ThisOne;
- PULONG Data;
-
- if (Argc > 1)
- {
- /* Get address */
- if (!KdbpGetHexNumber(Argv[1], &Address))
- {
- KdbpPrint("Invalid parameter: %s\n", Argv[0]);
- return TRUE;
- }
- }
-
- if (Argc > 2)
- {
- /* Get address */
- if (!KdbpGetHexNumber(Argv[1], &Flags))
- {
- KdbpPrint("Invalid parameter: %s\n", Argv[0]);
- return TRUE;
- }
- }
-
- /* Check if we got an address */
- if (Address != 0)
- {
- /* Get the base page */
- PoolPage = PAGE_ALIGN(Address);
- }
- else
- {
- KdbpPrint("Heap is unimplemented\n");
- return TRUE;
- }
-
- /* No paging support! */
- if (!MmIsAddressValid(PoolPage))
- {
- KdbpPrint("Address not accessible!\n");
- return TRUE;
- }
-
- /* Get pool type */
- if ((Address >= (ULONG_PTR)MmPagedPoolStart) && (Address <= (ULONG_PTR)MmPagedPoolEnd))
- KdbpPrint("Allocation is from PagedPool region\n");
- else if ((Address >= (ULONG_PTR)MmNonPagedPoolStart) && (Address <= (ULONG_PTR)MmNonPagedPoolEnd))
- KdbpPrint("Allocation is from NonPagedPool region\n");
- else
- {
- KdbpPrint("Address 0x%p is not within any pool!\n", (PVOID)Address);
- return TRUE;
- }
-
- /* Loop all entries of that page */
- Entry = PoolPage;
- do
- {
- /* Check if the address is within that entry */
- ThisOne = ((Address >= (ULONG_PTR)Entry) &&
- (Address < (ULONG_PTR)(Entry + Entry->BlockSize)));
-
- if (!(Flags & 1) || ThisOne)
- {
- /* Print the line */
- KdbpPrint("%c%p size: %4d previous size: %4d %s %.4s\n",
- ThisOne ? '*' : ' ', Entry, Entry->BlockSize, Entry->PreviousSize,
- (Flags & 0x80000000) ? "" : (Entry->PoolType ? "(Allocated)" : "(Free) "),
- (Flags & 0x80000000) ? "" : (PCHAR)&Entry->PoolTag);
- }
-
- if (Flags & 1)
- {
- Data = (PULONG)(Entry + 1);
- KdbpPrint(" %p %08lx %08lx %08lx %08lx\n"
- " %p %08lx %08lx %08lx %08lx\n",
- &Data[0], Data[0], Data[1], Data[2], Data[3],
- &Data[4], Data[4], Data[5], Data[6], Data[7]);
- }
-
- /* Go to next entry */
- Entry = POOL_BLOCK(Entry, Entry->BlockSize);
- }
- while ((Entry->BlockSize != 0) && ((ULONG_PTR)Entry < (ULONG_PTR)PoolPage + PAGE_SIZE));
-
- return TRUE;
-}
-
-static
-VOID
-ExpKdbgExtPoolUsedGetTag(PCHAR Arg, PULONG Tag, PULONG Mask)
-{
- CHAR Tmp[4];
- ULONG Len;
- USHORT i;
-
- /* Get the tag */
- Len = strlen(Arg);
- if (Len > 4)
- {
- Len = 4;
- }
-
- /* Generate the mask to have wildcards support */
- for (i = 0; i < Len; ++i)
- {
- Tmp[i] = Arg[i];
- if (Tmp[i] != '?')
- {
- *Mask |= (0xFF << i * 8);
- }
- }
-
- /* Get the tag in the ulong form */
- *Tag = *((PULONG)Tmp);
-}
-
-BOOLEAN
-ExpKdbgExtPoolUsed(
- ULONG Argc,
- PCHAR Argv[])
-{
- ULONG Tag = 0;
- ULONG Mask = 0;
- ULONG Flags = 0;
-
- if (Argc > 1)
- {
- /* If we have 2+ args, easy: flags then tag */
- if (Argc > 2)
- {
- ExpKdbgExtPoolUsedGetTag(Argv[2], &Tag, &Mask);
- if (!KdbpGetHexNumber(Argv[1], &Flags))
- {
- KdbpPrint("Invalid parameter: %s\n", Argv[0]);
- }
- }
- else
- {
- /* Otherwise, try to find out whether that's flags */
- if (strlen(Argv[1]) == 1 ||
- (strlen(Argv[1]) == 3 && Argv[1][0] == '0' && Argv[1][1] == 'x'))
- {
- /* Fallback: if reading flags failed, assume it's a tag */
- if (!KdbpGetHexNumber(Argv[1], &Flags))
- {
- ExpKdbgExtPoolUsedGetTag(Argv[1], &Tag, &Mask);
- }
- }
- /* Or tag */
- else
- {
- ExpKdbgExtPoolUsedGetTag(Argv[1], &Tag, &Mask);
- }
- }
- }
-
- /* Call the dumper */
- MiDumpPoolConsumers(TRUE, Tag, Mask, Flags);
-
- return TRUE;
-}
-
-#endif // DBG && KDBG
-
/* EOF */