Author: sir_richard Date: Mon Feb 27 08:21:15 2012 New Revision: 55879
URL: http://svn.reactos.org/svn/reactos?rev=55879&view=rev Log: [NTOS]: Handle memory allocation failures correctly, supporting all the required debug and caller flags (such as RAISE_ON_FAILURE). We no longer simply just return "NULL" in failure cases. [NTOS]: Implement counters for paged and nonpaged pool. First-stage setup now shows Kernel Pool values again, as does Task Manager. Fixes the regression introduced when pool/non-paged pool was no longer managed through "memory consumer" API. [NTOS]: Add more debugging paths and flags that were sent over from the "Aleksey Pool Patch". Most of them are not implemented. [NTOS]: Fix a missing case when a pool header check was not being done. [NTOS]: Check IRQL levels during pool allocation and free. With tags and the counters, pool leaks should be massively more debuggable now.
Modified: trunk/reactos/ntoskrnl/ex/sysinfo.c trunk/reactos/ntoskrnl/mm/ARM3/expool.c trunk/reactos/ntoskrnl/mm/ARM3/miarm.h
Modified: trunk/reactos/ntoskrnl/ex/sysinfo.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ex/sysinfo.c?rev=5... ============================================================================== --- trunk/reactos/ntoskrnl/ex/sysinfo.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/ex/sysinfo.c [iso-8859-1] Mon Feb 27 08:21:15 2012 @@ -456,7 +456,17 @@ #define SSI_DEF(n) \ static NTSTATUS SSI_USE(n) (PVOID Buffer, ULONG Size)
- +VOID +NTAPI +ExQueryPoolUsage(OUT PULONG PagedPoolPages, + OUT PULONG NonPagedPoolPages, + OUT PULONG PagedPoolAllocs, + OUT PULONG PagedPoolFrees, + OUT PULONG PagedPoolLookasideHits, + OUT PULONG NonPagedPoolAllocs, + OUT PULONG NonPagedPoolFrees, + OUT PULONG NonPagedPoolLookasideHits); + /* Class 0 - Basic Information */ QSI_DEF(SystemBasicInformation) { @@ -571,21 +581,27 @@ Spi->MappedPagesWriteCount = 0; /* FIXME */ Spi->MappedWriteIoCount = 0; /* FIXME */
- Spi->PagedPoolPages = 0; /* FIXME */ - Spi->PagedPoolAllocs = 0; /* FIXME */ - Spi->PagedPoolFrees = 0; /* FIXME */ - Spi->NonPagedPoolPages = 0; /* FIXME */ - Spi->NonPagedPoolAllocs = 0; /* FIXME */ - Spi->NonPagedPoolFrees = 0; /* FIXME */ - + Spi->PagedPoolPages = 0; + Spi->NonPagedPoolPages = 0; + Spi->PagedPoolAllocs = 0; + Spi->PagedPoolFrees = 0; + Spi->PagedPoolLookasideHits = 0; + Spi->NonPagedPoolAllocs = 0; + Spi->NonPagedPoolFrees = 0; + Spi->NonPagedPoolLookasideHits = 0; + ExQueryPoolUsage(&Spi->PagedPoolPages, + &Spi->NonPagedPoolPages, + &Spi->PagedPoolAllocs, + &Spi->PagedPoolFrees, + &Spi->PagedPoolLookasideHits, + &Spi->NonPagedPoolAllocs, + &Spi->NonPagedPoolFrees, + &Spi->NonPagedPoolLookasideHits); Spi->FreeSystemPtes = 0; /* FIXME */
Spi->ResidentSystemCodePage = 0; /* FIXME */
Spi->TotalSystemDriverPages = 0; /* FIXME */ - Spi->TotalSystemCodePages = 0; /* FIXME */ - Spi->NonPagedPoolLookasideHits = 0; /* FIXME */ - Spi->PagedPoolLookasideHits = 0; /* FIXME */ Spi->Spare3Count = 0; /* FIXME */
Spi->ResidentSystemCachePage = MiMemoryConsumers[MC_CACHE].PagesUsed;
Modified: trunk/reactos/ntoskrnl/mm/ARM3/expool.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/expool.c?r... ============================================================================== --- trunk/reactos/ntoskrnl/mm/ARM3/expool.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/mm/ARM3/expool.c [iso-8859-1] Mon Feb 27 08:21:15 2012 @@ -40,10 +40,12 @@ PPOOL_TRACKER_TABLE PoolTrackTable; PPOOL_TRACKER_BIG_PAGES PoolBigPageTable; KSPIN_LOCK ExpTaggedPoolLock; -ULONG PoolHitTag; +ULONG PoolHitTag = 'ht '; BOOLEAN ExStopBadTags; KSPIN_LOCK ExpLargePoolTableLock; LONG ExpPoolBigEntriesInUse; +ULONG ExpPoolFlags; +ULONG ExPoolFailures;
/* Pool block/header/list access macros */ #define POOL_ENTRY(x) (PPOOL_HEADER)((ULONG_PTR)(x) - sizeof(POOL_HEADER)) @@ -298,6 +300,31 @@ { /* Otherwise, the blocks are messed up */ KeBugCheckEx(BAD_POOL_HEADER, 10, (ULONG_PTR)Block, __LINE__, (ULONG_PTR)Entry); + } +} + +FORCEINLINE +VOID +ExpCheckPoolIrqlLevel(IN POOL_TYPE PoolType, + IN ULONG NumberOfBytes, + IN PVOID Entry) +{ + // + // Validate IRQL: It must be APC_LEVEL or lower for Paged Pool, and it must + // be DISPATCH_LEVEL or lower for Non Paged Pool + // + if (((PoolType & BASE_POOL_TYPE_MASK) == PagedPool) ? + (KeGetCurrentIrql() > APC_LEVEL) : + (KeGetCurrentIrql() > DISPATCH_LEVEL)) + { + // + // Take the system down + // + KeBugCheckEx(BAD_POOL_CALLER, + !Entry ? POOL_ALLOC_IRQL_INVALID : POOL_FREE_IRQL_INVALID, + KeGetCurrentIrql(), + PoolType, + !Entry ? NumberOfBytes : (ULONG_PTR)Entry); } }
@@ -938,6 +965,7 @@ // PoolVector[PagedPool] = Descriptor; ExpPagedPoolMutex = (PKGUARDED_MUTEX)(Descriptor + 1); + ExpPagedPoolDescriptor[0] = Descriptor; KeInitializeGuardedMutex(ExpPagedPoolMutex); ExInitializePoolDescriptor(Descriptor, PagedPool, @@ -1305,6 +1333,70 @@ return PoolTag; }
+VOID +NTAPI +ExQueryPoolUsage(OUT PULONG PagedPoolPages, + OUT PULONG NonPagedPoolPages, + OUT PULONG PagedPoolAllocs, + OUT PULONG PagedPoolFrees, + OUT PULONG PagedPoolLookasideHits, + OUT PULONG NonPagedPoolAllocs, + OUT PULONG NonPagedPoolFrees, + OUT PULONG NonPagedPoolLookasideHits) +{ + ULONG i; + PPOOL_DESCRIPTOR PoolDesc; + + // + // Assume all failures + // + *PagedPoolPages = 0; + *PagedPoolAllocs = 0; + *PagedPoolFrees = 0; + + // + // Tally up the totals for all the apged pool + // + for (i = 0; i < ExpNumberOfPagedPools + 1; i++) + { + PoolDesc = ExpPagedPoolDescriptor[i]; + *PagedPoolPages += PoolDesc->TotalPages + PoolDesc->TotalBigPages; + *PagedPoolAllocs += PoolDesc->RunningAllocs; + *PagedPoolFrees += PoolDesc->RunningDeAllocs; + } + + // + // The first non-paged pool has a hardcoded well-known descriptor name + // + PoolDesc = &NonPagedPoolDescriptor; + *NonPagedPoolPages = PoolDesc->TotalPages + PoolDesc->TotalBigPages; + *NonPagedPoolAllocs = PoolDesc->RunningAllocs; + *NonPagedPoolFrees = PoolDesc->RunningDeAllocs; + + // + // If the system has more than one non-paged pool, copy the other descriptor + // totals as well + // +#if 0 + if (ExpNumberOfNonPagedPools > 1) + { + for (i = 0; i < ExpNumberOfNonPagedPools; i++) + { + PoolDesc = ExpNonPagedPoolDescriptor[i]; + *NonPagedPoolPages += PoolDesc->TotalPages + PoolDesc->TotalBigPages; + *NonPagedPoolAllocs += PoolDesc->RunningAllocs; + *NonPagedPoolFrees += PoolDesc->RunningDeAllocs; + } + } +#endif + + // + // FIXME: Not yet supported + // + *NonPagedPoolLookasideHits += 0; + *PagedPoolLookasideHits += 0; +} + /* PUBLIC FUNCTIONS ***********************************************************/
/* @@ -1321,6 +1413,7 @@ PPOOL_HEADER Entry, NextEntry, FragmentEntry; KIRQL OldIrql; USHORT BlockSize, i; + ULONG OriginalType;
// // Some sanity checks @@ -1328,27 +1421,55 @@ ASSERT(Tag != 0); ASSERT(Tag != ' GIB'); ASSERT(NumberOfBytes != 0); + ExpCheckPoolIrqlLevel(PoolType, NumberOfBytes, NULL); + + // + // Not supported in ReactOS + // + ASSERT(!(PoolType & SESSION_POOL_MASK)); + + // + // Check if verifier or special pool is enabled + // + if (ExpPoolFlags & (POOL_FLAG_VERIFIER | POOL_FLAG_SPECIAL_POOL)) + { + // + // For verifier, we should call the verification routine + // + if (ExpPoolFlags & POOL_FLAG_VERIFIER) + { + DPRINT1("Driver Verifier is not yet supported\n"); + } + + // + // For special pool, we check if this is a suitable allocation and do + // the special allocation if needed + // + if (ExpPoolFlags & POOL_FLAG_SPECIAL_POOL) + { + // + // Check if this is a special pool allocation + // + if (MmUseSpecialPool(NumberOfBytes, Tag)) + { + // + // Try to allocate using special pool + // + Entry = MmAllocateSpecialPool(NumberOfBytes, Tag, PoolType, 2); + if (Entry) return Entry; + } + } + }
// // Get the pool type and its corresponding vector for this request // + OriginalType = PoolType; PoolType = PoolType & BASE_POOL_TYPE_MASK; PoolDesc = PoolVector[PoolType]; ASSERT(PoolDesc != NULL);
// - // Check if this is a special pool allocation - // - if (MmUseSpecialPool(NumberOfBytes, Tag)) - { - // - // Try to allocate using special pool - // - Entry = MmAllocateSpecialPool(NumberOfBytes, Tag, PoolType, 2); - if (Entry) return Entry; - } - - // // Check if this is a big page allocation // if (NumberOfBytes > POOL_MAX_ALLOC) @@ -1356,15 +1477,68 @@ // // Allocate pages for it // - Entry = MiAllocatePoolPages(PoolType, NumberOfBytes); - if (!Entry) return NULL; - + Entry = MiAllocatePoolPages(OriginalType, NumberOfBytes); + if (!Entry) + { + // + // Must succeed pool is deprecated, but still supported. These allocation + // failures must cause an immediate bugcheck + // + if (OriginalType & MUST_SUCCEED_POOL_MASK) + { + KeBugCheckEx(MUST_SUCCEED_POOL_EMPTY, + NumberOfBytes, + NonPagedPoolDescriptor.TotalPages, + NonPagedPoolDescriptor.TotalBigPages, + 0); + } + + // + // Internal debugging + // + ExPoolFailures++; + + // + // This flag requests printing failures, and can also further specify + // breaking on failures + // + if (ExpPoolFlags & POOL_FLAG_DBGPRINT_ON_FAILURE) + { + DPRINT1("EX: ExAllocatePool (%p, 0x%x) returning NULL\n", + NumberOfBytes, + OriginalType); + if (ExpPoolFlags & POOL_FLAG_CRASH_ON_FAILURE) DbgBreakPoint(); + } + + // + // Finally, this flag requests an exception, which we are more than + // happy to raise! + // + if (OriginalType & POOL_RAISE_IF_ALLOCATION_FAILURE) + { + ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); + } + } + + // + // Increment required counters + // + InterlockedExchangeAdd((PLONG)&PoolDesc->TotalBigPages, BYTES_TO_PAGES(NumberOfBytes)); + InterlockedExchangeAddSizeT(&PoolDesc->TotalBytes, NumberOfBytes); + InterlockedIncrement((PLONG)&PoolDesc->RunningAllocs); + // // Add a tag for the big page allocation and switch to the generic "BIG" // tag if we failed to do so, then insert a tracker for this alloation. // - if (!ExpAddTagForBigPages(Entry, Tag, BYTES_TO_PAGES(NumberOfBytes), PoolType)) Tag = ' GIB'; - ExpInsertPoolTracker(Tag, ROUND_TO_PAGES(NumberOfBytes), PoolType); + if (!ExpAddTagForBigPages(Entry, + Tag, + BYTES_TO_PAGES(NumberOfBytes), + OriginalType)) + { + Tag = ' GIB'; + } + ExpInsertPoolTracker(Tag, ROUND_TO_PAGES(NumberOfBytes), OriginalType); return Entry; }
@@ -1550,11 +1724,17 @@ ExUnlockPool(PoolDesc, OldIrql);
// + // Increment required counters + // + InterlockedExchangeAddSizeT(&PoolDesc->TotalBytes, Entry->BlockSize * POOL_BLOCK_SIZE); + InterlockedIncrement((PLONG)&PoolDesc->RunningAllocs); + + // // Track this allocation // ExpInsertPoolTracker(Tag, Entry->BlockSize * POOL_BLOCK_SIZE, - PoolType); + OriginalType);
// // Return the pool allocation @@ -1570,8 +1750,56 @@ // There were no free entries left, so we have to allocate a new fresh page // Entry = MiAllocatePoolPages(PoolType, PAGE_SIZE); - if (Entry == NULL) return NULL; - + if (!Entry) + { + // + // Must succeed pool is deprecated, but still supported. These allocation + // failures must cause an immediate bugcheck + // + if (OriginalType & MUST_SUCCEED_POOL_MASK) + { + KeBugCheckEx(MUST_SUCCEED_POOL_EMPTY, + PAGE_SIZE, + NonPagedPoolDescriptor.TotalPages, + NonPagedPoolDescriptor.TotalBigPages, + 0); + } + + // + // Internal debugging + // + ExPoolFailures++; + + // + // This flag requests printing failures, and can also further specify + // breaking on failures + // + if (ExpPoolFlags & POOL_FLAG_DBGPRINT_ON_FAILURE) + { + DPRINT1("EX: ExAllocatePool (%p, 0x%x) returning NULL\n", + NumberOfBytes, + OriginalType); + if (ExpPoolFlags & POOL_FLAG_CRASH_ON_FAILURE) DbgBreakPoint(); + } + + // + // Finally, this flag requests an exception, which we are more than + // happy to raise! + // + if (OriginalType & POOL_RAISE_IF_ALLOCATION_FAILURE) + { + ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); + } + + // + // Return NULL to the caller in all other cases + // + return NULL; + } + + // + // Setup the entry data + // Entry->Ulong1 = 0; Entry->BlockSize = i; Entry->PoolType = PoolType + 1; @@ -1589,6 +1817,12 @@ FragmentEntry->PreviousSize = i;
// + // Increment required counters + // + InterlockedIncrement((PLONG)&PoolDesc->TotalPages); + InterlockedExchangeAddSizeT(&PoolDesc->TotalBytes, Entry->BlockSize * POOL_BLOCK_SIZE); + + // // Now check if enough free bytes remained for us to have a "full" entry, // which contains enough bytes for a linked list and thus can be used for // allocations (up to 8 bytes...) @@ -1614,10 +1848,18 @@ ExpCheckPoolBlocks(Entry); ExUnlockPool(PoolDesc, OldIrql); } - - // - // Track this allocation - // + else + { + // + // Simply do a sanity check + // + ExpCheckPoolBlocks(Entry); + } + + // + // Increment performance counters and track this allocation + // + InterlockedIncrement((PLONG)&PoolDesc->RunningAllocs); ExpInsertPoolTracker(Tag, Entry->BlockSize * POOL_BLOCK_SIZE, PoolType); @@ -1659,18 +1901,67 @@ PPOOL_DESCRIPTOR PoolDesc; ULONG Tag; BOOLEAN Combined = FALSE; - PFN_NUMBER PageCount; - - // - // Check if it was allocated from a special pool - // - if (MmIsSpecialPoolAddress(P)) - { - // - // It is, so handle it via special pool free routine - // - MmFreeSpecialPool(P); - return; + PFN_NUMBER PageCount, RealPageCount; + + // + // Check if any of the debug flags are enabled + // + if (ExpPoolFlags & (POOL_FLAG_CHECK_TIMERS | + POOL_FLAG_CHECK_WORKERS | + POOL_FLAG_CHECK_RESOURCES | + POOL_FLAG_VERIFIER | + POOL_FLAG_CHECK_DEADLOCK | + POOL_FLAG_SPECIAL_POOL)) + { + // + // Check if special pool is enabled + // + if (ExpPoolFlags & POOL_FLAG_SPECIAL_POOL) + { + // + // Check if it was allocated from a special pool + // + if (MmIsSpecialPoolAddress(P)) + { + // + // Was deadlock verification also enabled? We can do some extra + // checks at this point + // + if (ExpPoolFlags & POOL_FLAG_CHECK_DEADLOCK) + { + DPRINT1("Verifier not yet supported\n"); + } + + // + // It is, so handle it via special pool free routine + // + MmFreeSpecialPool(P); + return; + } + } + + // + // For non-big page allocations, we'll do a bunch of checks in here + // + if (PAGE_ALIGN(P) != P) + { + // + // Get the entry for this pool allocation + // The pointer math here may look wrong or confusing, but it is quite right + // + Entry = P; + Entry--; + + // + // Get the pool type + // + PoolType = (Entry->PoolType - 1) & BASE_POOL_TYPE_MASK; + + // + // FIXME: Many other debugging checks go here + // + ExpCheckPoolIrqlLevel(PoolType, 0, P); + } }
// @@ -1691,6 +1982,7 @@ // the PFN database. // PoolType = MmDeterminePoolType(P); + ExpCheckPoolIrqlLevel(PoolType, 0, P); Tag = ExpFindAndRemoveTagBigPages(P, &PageCount, PoolType); if (!Tag) { @@ -1708,7 +2000,42 @@ // tracker now // ExpRemovePoolTracker(Tag, PageCount << PAGE_SHIFT, PoolType); - MiFreePoolPages(P); + + // + // Check if any of the debug flags are enabled + // + if (ExpPoolFlags & (POOL_FLAG_CHECK_TIMERS | + POOL_FLAG_CHECK_WORKERS | + POOL_FLAG_CHECK_RESOURCES | + POOL_FLAG_CHECK_DEADLOCK)) + { + // + // Was deadlock verification also enabled? We can do some extra + // checks at this point + // + if (ExpPoolFlags & POOL_FLAG_CHECK_DEADLOCK) + { + DPRINT1("Verifier not yet supported\n"); + } + + // + // FIXME: Many debugging checks go here + // + } + + // + // Update counters + // + PoolDesc = PoolVector[PoolType]; + InterlockedIncrement((PLONG)&PoolDesc->RunningDeAllocs); + InterlockedExchangeAddSizeT(&PoolDesc->TotalBytes, -PageCount << PAGE_SHIFT); + + // + // Do the real free now and update the last counter with the big page count + // + RealPageCount = MiFreePoolPages(P); + ASSERT(RealPageCount == PageCount); + InterlockedExchangeAdd((PLONG)&PoolDesc->TotalBigPages, -RealPageCount); return; }
@@ -1728,6 +2055,11 @@ PoolDesc = PoolVector[PoolType];
// + // Make sure that the IRQL makes sense + // + ExpCheckPoolIrqlLevel(PoolType, 0, P); + + // // Get the pool tag and get rid of the PROTECTED_POOL flag // Tag = Entry->PoolTag; @@ -1744,6 +2076,12 @@ // Get the pointer to the next entry // NextEntry = POOL_BLOCK(Entry, BlockSize); + + // + // Update performance counters + // + InterlockedIncrement((PLONG)&PoolDesc->RunningDeAllocs); + InterlockedExchangeAddSizeT(&PoolDesc->TotalBytes, -BlockSize * sizeof(POOL_BLOCK_SIZE));
// // Acquire the pool lock @@ -1854,9 +2192,11 @@ (PAGE_ALIGN(POOL_NEXT_BLOCK(Entry)) == POOL_NEXT_BLOCK(Entry))) { // - // In this case, release the pool lock, and free the page + // In this case, release the pool lock, update the performance counter, + // and free the page // ExUnlockPool(PoolDesc, OldIrql); + InterlockedExchangeAdd((PLONG)&PoolDesc->TotalPages, -1); MiFreePoolPages(Entry); return; }
Modified: trunk/reactos/ntoskrnl/mm/ARM3/miarm.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/miarm.h?re... ============================================================================== --- trunk/reactos/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] Mon Feb 27 08:21:15 2012 @@ -286,6 +286,40 @@ #define POOL_LISTS_PER_PAGE (PAGE_SIZE / POOL_BLOCK_SIZE) #define BASE_POOL_TYPE_MASK 1 #define POOL_MAX_ALLOC (PAGE_SIZE - (sizeof(POOL_HEADER) + POOL_BLOCK_SIZE)) + +// +// Pool debugging/analysis/tracing flags +// +#define POOL_FLAG_CHECK_TIMERS 0x1 +#define POOL_FLAG_CHECK_WORKERS 0x2 +#define POOL_FLAG_CHECK_RESOURCES 0x4 +#define POOL_FLAG_VERIFIER 0x8 +#define POOL_FLAG_CHECK_DEADLOCK 0x10 +#define POOL_FLAG_SPECIAL_POOL 0x20 +#define POOL_FLAG_DBGPRINT_ON_FAILURE 0x40 +#define POOL_FLAG_CRASH_ON_FAILURE 0x80 + +// +// BAD_POOL_HEADER codes during pool bugcheck +// +#define POOL_CORRUPTED_LIST 3 +#define POOL_SIZE_OR_INDEX_MISMATCH 5 +#define POOL_ENTRIES_NOT_ALIGNED_PREVIOUS 6 +#define POOL_HEADER_NOT_ALIGNED 7 +#define POOL_HEADER_IS_ZERO 8 +#define POOL_ENTRIES_NOT_ALIGNED_NEXT 9 +#define POOL_ENTRY_NOT_FOUND 10 + +// +// BAD_POOL_CALLER codes during pool bugcheck +// +#define POOL_ENTRY_CORRUPTED 1 +#define POOL_ENTRY_ALREADY_FREE 6 +#define POOL_ENTRY_NOT_ALLOCATED 7 +#define POOL_ALLOC_IRQL_INVALID 8 +#define POOL_FREE_IRQL_INVALID 9 +#define POOL_BILLED_PROCESS_INVALID 13 +#define POOL_HEADER_SIZE_INVALID 32
typedef struct _POOL_DESCRIPTOR {