completely rewritten PagedPool.
[reactos.git] / reactos / ntoskrnl / mm / ppool.c
index cdcd2b1..ef93d52 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: ppool.c,v 1.28 2004/04/10 22:35:25 gdalsnes Exp $
+/* $Id: ppool.c,v 1.37 2004/12/17 13:20:05 royce Exp $
  *
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS kernel
  * PROGRAMMER:      David Welch (welch@mcmail.com)
  * UPDATE HISTORY:
  *                  Created 22/05/98
+ *                  Complete Rewrite Dec 2004 by Royce Mitchell III
  */
 
 /* INCLUDES *****************************************************************/
 
-#include <ddk/ntddk.h>
-#include <internal/pool.h>
-#include <internal/mm.h>
-
+#ifdef PPOOL_UMODE_TEST
+#include "ppool_umode.h"
+#else//PPOOL_UMODE_TEST
+#include <ntoskrnl.h>
 #define NDEBUG
 #include <internal/debug.h>
+#endif//PPOOL_UMODE_TEST
 
-/* GLOBALS *******************************************************************/
-
-/* Define to enable strict checking of the paged pool on every allocation */
-/* #define ENABLE_VALIDATE_POOL */
+#undef ASSERT
+#define ASSERT(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d\n", __FILE__,__LINE__); KeBugCheck(0); }
 
-#undef assert
-#define assert(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d\n", __FILE__,__LINE__); KeBugCheck(0); }
-#define ASSERT_SIZE(n) assert ( (n) <= MmPagedPoolSize && (n) >= 0 )
-#define ASSERT_PTR(p) assert ( ((size_t)(p)) >= ((size_t)MmPagedPoolBase) && ((size_t)(p)) < ((size_t)((size_t)MmPagedPoolBase+MmPagedPoolSize)) )
+// enable "magic"
+//#define R_MAGIC
+#define R_MUTEX FAST_MUTEX
+#define R_ACQUIRE_MUTEX(pool) /*DPRINT1("Acquiring PPool Mutex\n");*/ ExAcquireFastMutex(&pool->Mutex)
+#define R_RELEASE_MUTEX(pool) /*DPRINT1("Releasing PPool Mutex\n");*/ ExReleaseFastMutex(&pool->Mutex)
+#define R_PRINT_ADDRESS(addr) 0
+#define R_PANIC() KeBugCheck(0)
+#define R_DEBUG DbgPrint
+#define R_EXTRA_STACK_UP 2
 
-// to disable buffer over/under-run detection, set the following macro to 0
-#define MM_PPOOL_REDZONE_BYTES 4
-#define MM_PPOOL_REDZONE_VALUE 0xCD
+#include "RPoolMgr.h"
 
-typedef struct _MM_PPOOL_FREE_BLOCK_HEADER
-{
-   ULONG Size;
-   struct _MM_PPOOL_FREE_BLOCK_HEADER* NextFree;
-}
-MM_PPOOL_FREE_BLOCK_HEADER, *PMM_PPOOL_FREE_BLOCK_HEADER;
-
-typedef struct _MM_PPOOL_USED_BLOCK_HEADER
-{
-   ULONG Size;
-#if MM_PPOOL_REDZONE_BYTES
-
-   ULONG UserSize; // how many bytes the user actually asked for...
-   struct _MM_PPOOL_USED_BLOCK_HEADER* NextUsed;
-#endif//MM_PPOOL_REDZONE_BYTES
-}
-MM_PPOOL_USED_BLOCK_HEADER, *PMM_PPOOL_USED_BLOCK_HEADER;
+/* GLOBALS *******************************************************************/
 
 PVOID MmPagedPoolBase;
 ULONG MmPagedPoolSize;
-static FAST_MUTEX MmPagedPoolLock;
-static PMM_PPOOL_FREE_BLOCK_HEADER MmPagedPoolFirstFreeBlock;
-#if MM_PPOOL_REDZONE_BYTES
-static PMM_PPOOL_USED_BLOCK_HEADER MmPagedPoolFirstUsedBlock;
-#endif//MM_PPOOL_REDZONE_BYTES
+ULONG MmTotalPagedPoolQuota = 0; // TODO FIXME commented out until we use it
+static PR_POOL MmPagedPool = NULL;
 
 /* FUNCTIONS *****************************************************************/
 
-inline static void* block_to_address ( PVOID blk )
-/*
- * FUNCTION: Translate a block header address to the corresponding block
- * address (internal)
- */
-{
-   return ( (void *) ((char*)blk + sizeof(MM_PPOOL_USED_BLOCK_HEADER) + MM_PPOOL_REDZONE_BYTES) );
-}
-
-inline static PMM_PPOOL_USED_BLOCK_HEADER address_to_block(PVOID addr)
-{
-   return (PMM_PPOOL_USED_BLOCK_HEADER)
-          ( ((char*)addr) - sizeof(MM_PPOOL_USED_BLOCK_HEADER) - MM_PPOOL_REDZONE_BYTES );
-}
-
 VOID INIT_FUNCTION
-MmInitializePagedPool(VOID)
+MmInitializePagedPool()
 {
-   MmPagedPoolFirstFreeBlock = (PMM_PPOOL_FREE_BLOCK_HEADER)MmPagedPoolBase;
-   /*
-    * We are still at a high IRQL level at this point so explicitly commit
-    * the first page of the paged pool before writing the first block header.
-    */
-   MmCommitPagedPoolAddress((PVOID)MmPagedPoolFirstFreeBlock, FALSE);
-   MmPagedPoolFirstFreeBlock->Size = MmPagedPoolSize;
-   MmPagedPoolFirstFreeBlock->NextFree = NULL;
-
-#if MM_PPOOL_REDZONE_BYTES
-
-   MmPagedPoolFirstUsedBlock = NULL;
-#endif//MM_PPOOL_REDZONE_BYTES
-
-   ExInitializeFastMutex(&MmPagedPoolLock);
-}
-
-#ifdef ENABLE_VALIDATE_POOL
-static void VerifyPagedPool ( int line )
-{
-   PMM_PPOOL_FREE_BLOCK_HEADER p = MmPagedPoolFirstFreeBlock;
-   int count = 0;
-   DPRINT ( "VerifyPagedPool(%i):\n", line );
-   while ( p )
-   {
-      DPRINT ( "  0x%x: %lu bytes (next 0x%x)\n", p, p->Size, p->NextFree );
-      ASSERT_PTR(p);
-      ASSERT_SIZE(p->Size);
-      count++;
-      p = p->NextFree;
-   }
-   DPRINT ( "VerifyPagedPool(%i): (%lu blocks)\n", line, count );
-}
-#define VerifyPagedPool() VerifyPagedPool(__LINE__)
-#else
-#define VerifyPagedPool()
-#endif
-
-VOID STDCALL
-MmDbgPagedPoolRedZoneCheck ( const char* file, int line )
-{
-#if MM_PPOOL_REDZONE_BYTES
-   PMM_PPOOL_USED_BLOCK_HEADER pUsed = MmPagedPoolFirstUsedBlock;
-   int i;
-   BOOL bLow = TRUE;
-   BOOL bHigh = TRUE;
-
-   while ( pUsed )
-   {
-      PUCHAR Addr = (PUCHAR)block_to_address(pUsed);
-      for ( i = 0; i < MM_PPOOL_REDZONE_BYTES; i++ )
-      {
-         bLow = bLow && ( *(Addr-i-1) == MM_PPOOL_REDZONE_VALUE );
-         bHigh = bHigh && ( *(Addr+pUsed->UserSize+i) == MM_PPOOL_REDZONE_VALUE );
-      }
-      if ( !bLow || !bHigh )
-      {
-         const char* violation = "High and Low-side";
-         if ( bHigh ) // high is okay, so it was just low failed
-            violation = "Low-side";
-         else if ( bLow ) // low side is okay, so it was just high failed
-            violation = "High-side";
-         DbgPrint("%s(%i): %s redzone violation detected for paged pool address 0x%x\n",
-                  file, line, violation, Addr );
-         KEBUGCHECK(0);
-      }
-      pUsed = pUsed->NextUsed;
-   }
-#endif//MM_PPOOL_REDZONE_BYTES
+       /*
+        * We are still at a high IRQL level at this point so explicitly commit
+        * the first page of the paged pool before writing the first block header.
+        */
+       MmCommitPagedPoolAddress ( (PVOID)MmPagedPoolBase, FALSE );
+
+       MmPagedPool = RPoolInit ( MmPagedPoolBase,
+               MmPagedPoolSize,
+               MM_POOL_ALIGNMENT,
+               MM_CACHE_LINE_SIZE,
+               PAGE_SIZE );
+
+       ExInitializeFastMutex(&MmPagedPool->Mutex);
 }
 
 /**********************************************************************
@@ -164,402 +77,169 @@ ExAllocatePagedPoolWithTag (IN POOL_TYPE PoolType,
                             IN ULONG  NumberOfBytes,
                             IN ULONG  Tag)
 {
-   PMM_PPOOL_FREE_BLOCK_HEADER BestBlock;
-   PMM_PPOOL_FREE_BLOCK_HEADER CurrentBlock;
-   ULONG BlockSize;
-   PMM_PPOOL_USED_BLOCK_HEADER NewBlock;
-   PMM_PPOOL_FREE_BLOCK_HEADER NextBlock;
-   PMM_PPOOL_FREE_BLOCK_HEADER PreviousBlock;
-   PMM_PPOOL_FREE_BLOCK_HEADER BestPreviousBlock;
-   PVOID BlockAddress;
-   ULONG Alignment;
-
-   ExAcquireFastMutex(&MmPagedPoolLock);
-
-   /*
-    * Don't bother allocating anything for a zero-byte block.
-    */
-   if (NumberOfBytes == 0)
-   {
-      MmDbgPagedPoolRedZoneCheck(__FILE__,__LINE__);
-      ExReleaseFastMutex(&MmPagedPoolLock);
-      return(NULL);
-   }
-
-   DPRINT ( "ExAllocatePagedPoolWithTag(%i,%lu,%lu)\n", PoolType, NumberOfBytes, Tag );
-   VerifyPagedPool();
-
-   if (NumberOfBytes >= PAGE_SIZE)
-   {
-      Alignment = PAGE_SIZE;
-   }
-   else if (PoolType == PagedPoolCacheAligned)
-   {
-      Alignment = MM_CACHE_LINE_SIZE;
-   }
-   else
-   {
-      Alignment = MM_POOL_ALIGNMENT;
-   }
-
-   /*
-    * Calculate the total number of bytes we will need.
-    */
-   BlockSize = NumberOfBytes + sizeof(MM_PPOOL_USED_BLOCK_HEADER) + 2*MM_PPOOL_REDZONE_BYTES;
-   if (BlockSize < sizeof(MM_PPOOL_FREE_BLOCK_HEADER))
-   {
-      /* At least we need the size of the free block header. */
-      BlockSize = sizeof(MM_PPOOL_FREE_BLOCK_HEADER);
-   }
-
-
-   /*
-    * Find the best-fitting block.
-    */
-   PreviousBlock = NULL;
-   BestPreviousBlock = BestBlock = NULL;
-   CurrentBlock = MmPagedPoolFirstFreeBlock;
-   if ( Alignment > 0 )
-   {
-      PVOID BestAlignedAddr = NULL;
-      while ( CurrentBlock != NULL )
-      {
-         PVOID Addr = block_to_address(CurrentBlock);
-         PVOID CurrentBlockEnd = (char*)CurrentBlock + CurrentBlock->Size;
-         /* calculate last size-aligned address available within this block */
-         PVOID AlignedAddr = MM_ROUND_DOWN((char*)CurrentBlockEnd-NumberOfBytes-MM_PPOOL_REDZONE_BYTES, Alignment);
-         assert ( (char*)AlignedAddr+NumberOfBytes+MM_PPOOL_REDZONE_BYTES <= (char*)CurrentBlockEnd );
-
-         /* special case, this address is already size-aligned, and the right size */
-         if ( Addr == AlignedAddr )
-         {
-            BestAlignedAddr = AlignedAddr;
-            BestPreviousBlock = PreviousBlock;
-            BestBlock = CurrentBlock;
-            break;
-         }
-         else if ( Addr < (PVOID)address_to_block(AlignedAddr) )
-         {
-            /*
-             * there's enough room to allocate our size-aligned memory out
-             * of this block, see if it's a better choice than any previous
-             * finds
-             */
-            if ( BestBlock == NULL || BestBlock->Size > CurrentBlock->Size )
-            {
-               BestAlignedAddr = AlignedAddr;
-               BestPreviousBlock = PreviousBlock;
-               BestBlock = CurrentBlock;
-            }
-         }
-
-         PreviousBlock = CurrentBlock;
-         CurrentBlock = CurrentBlock->NextFree;
-      }
-
-      /*
-       * we found a best block can/should we chop a few bytes off the beginning
-       * into a separate memory block?
-       */
-      if ( BestBlock != NULL )
-      {
-         PVOID Addr = block_to_address(BestBlock);
-         if ( BestAlignedAddr != Addr )
-         {
-            PMM_PPOOL_FREE_BLOCK_HEADER NewFreeBlock =
-               (PMM_PPOOL_FREE_BLOCK_HEADER)address_to_block(BestAlignedAddr);
-            assert ( BestAlignedAddr > Addr );
-            NewFreeBlock->Size = (char*)Addr + BestBlock->Size - (char*)BestAlignedAddr;
-            ASSERT_SIZE(NewFreeBlock->Size);
-            BestBlock->Size = (size_t)NewFreeBlock - (size_t)Addr;
-            ASSERT_SIZE(BestBlock->Size);
-
-            DPRINT ( "breaking off preceding bytes into their own block...\n" );
-            DPRINT ( "NewFreeBlock 0x%x Size %lu (Old Block's new size %lu) NextFree 0x%x\n",
-                     NewFreeBlock, NewFreeBlock->Size, BestBlock->Size, BestBlock->NextFree );
-
-            /* insert the new block into the chain */
-            NewFreeBlock->NextFree = BestBlock->NextFree;
-            BestBlock->NextFree = NewFreeBlock;
-
-            /* we want the following code to use our size-aligned block */
-            BestPreviousBlock = BestBlock;
-            BestBlock = NewFreeBlock;
-
-            //VerifyPagedPool();
-         }
-      }
-   }
-   /*
-    * non-size-aligned block search
-    */
-   else
-      while ( CurrentBlock != NULL )
-      {
-         if (    CurrentBlock->Size >= BlockSize
-                 && ( BestBlock == NULL || BestBlock->Size > CurrentBlock->Size )
-            )
-         {
-            BestPreviousBlock = PreviousBlock;
-            BestBlock = CurrentBlock;
-         }
-
-         PreviousBlock = CurrentBlock;
-         CurrentBlock = CurrentBlock->NextFree;
-      }
-
-   /*
-    * We didn't find anything suitable at all.
-    */
-   if (BestBlock == NULL)
-   {
-      DPRINT1("Trying to allocate %lu bytes from paged pool - nothing suitable found, returning NULL\n",
-              NumberOfBytes );
-      ExReleaseFastMutex(&MmPagedPoolLock);
-      return(NULL);
-   }
-
-   DPRINT("BestBlock 0x%x NextFree 0x%x\n", BestBlock, BestBlock->NextFree );
-
-   //VerifyPagedPool();
-
-   /*
-    * Is there enough space to create a second block from the unused portion.
-    */
-   if ( BestBlock->Size > BlockSize
-         && (BestBlock->Size - BlockSize) > sizeof(MM_PPOOL_FREE_BLOCK_HEADER)
-      )
-   {
-      ULONG NewSize = BestBlock->Size - BlockSize;
-      ASSERT_SIZE ( NewSize );
-
-      //DPRINT("creating 2nd block from unused portion\n");
-      DPRINT("BestBlock 0x%x Size 0x%x BlockSize 0x%x NewSize 0x%x\n",
-             BestBlock, BestBlock->Size, BlockSize, NewSize );
-
-      /*
-       * Create the new free block.
-       */
-      //DPRINT("creating the new free block");
-      NextBlock = (PMM_PPOOL_FREE_BLOCK_HEADER)((char*)BestBlock + BlockSize);
-      //DPRINT(".");
-      NextBlock->Size = NewSize;
-      ASSERT_SIZE ( NextBlock->Size );
-      //DPRINT(".");
-      NextBlock->NextFree = BestBlock->NextFree;
-      //DPRINT(".\n");
-
-      /*
-       * Replace the old free block with it.
-       */
-      //DPRINT("replacing old free block with it");
-      if (BestPreviousBlock == NULL)
-      {
-         //DPRINT("(from beginning)");
-         MmPagedPoolFirstFreeBlock = NextBlock;
-      }
-      else
-      {
-         //DPRINT("(from previous)");
-         BestPreviousBlock->NextFree = NextBlock;
-      }
-      //DPRINT(".\n");
-
-      /*
-       * Create the new used block header.
-       */
-      //DPRINT("create new used block header");
-      NewBlock = (PMM_PPOOL_USED_BLOCK_HEADER)BestBlock;
-      //DPRINT(".");
-      NewBlock->Size = BlockSize;
-      ASSERT_SIZE ( NewBlock->Size );
-      //DPRINT(".\n");
-   }
-   else
-   {
-      ULONG NewSize = BestBlock->Size;
-
-      /*
-       * Remove the selected block from the list of free blocks.
-       */
-      //DPRINT ( "Removing selected block from free block list\n" );
-      if (BestPreviousBlock == NULL)
-      {
-         MmPagedPoolFirstFreeBlock = BestBlock->NextFree;
-      }
-      else
-      {
-         BestPreviousBlock->NextFree = BestBlock->NextFree;
-      }
-
-      /*
-       * Set up the header of the new block
-       */
-      NewBlock = (PMM_PPOOL_USED_BLOCK_HEADER)BestBlock;
-      NewBlock->Size = NewSize;
-      ASSERT_SIZE ( NewBlock->Size );
-   }
-
-#if MM_PPOOL_REDZONE_BYTES
-   // now add the block to the used block list
-   NewBlock->NextUsed = MmPagedPoolFirstUsedBlock;
-   MmPagedPoolFirstUsedBlock = NewBlock;
-#endif//MM_PPOOL_REDZONE_BYTES
-
-   VerifyPagedPool();
-
-   ExReleaseFastMutex(&MmPagedPoolLock);
-
-   BlockAddress = block_to_address ( NewBlock );
-   /*  RtlZeroMemory(BlockAddress, NumberOfBytes);*/
-
-#if MM_PPOOL_REDZONE_BYTES
-
-   NewBlock->UserSize = NumberOfBytes;
-   // write out buffer-overrun detection bytes
-   {
-      PUCHAR Addr = (PUCHAR)BlockAddress;
-      //DbgPrint ( "writing buffer-overrun detection bytes" );
-      memset ( Addr - MM_PPOOL_REDZONE_BYTES,
-               MM_PPOOL_REDZONE_VALUE, MM_PPOOL_REDZONE_BYTES );
-      memset ( Addr + NewBlock->UserSize, MM_PPOOL_REDZONE_VALUE,
-               MM_PPOOL_REDZONE_BYTES );
-      /*for ( i = 0; i < MM_PPOOL_REDZONE_BYTES; i++ )
-      {
-        //DbgPrint(".");
-        *(Addr-i-1) = 0xCD;
-        //DbgPrint("o");
-        *(Addr+NewBlock->UserSize+i) = 0xCD;
-      }*/
-      //DbgPrint ( "done!\n" );
-   }
-
-#endif//MM_PPOOL_REDZONE_BYTES
-
-   return(BlockAddress);
+       int align;
+
+       if ( NumberOfBytes >= PAGE_SIZE )
+               align = 2;
+       else if ( PoolType == PagedPoolCacheAligned )
+               align = 1;
+       else
+               align = 0;
+
+       ASSERT_IRQL(APC_LEVEL);
+
+       return RPoolAlloc ( MmPagedPool, NumberOfBytes, Tag, align );
 }
 
 VOID STDCALL
 ExFreePagedPool(IN PVOID Block)
 {
-   PMM_PPOOL_FREE_BLOCK_HEADER PreviousBlock;
-   PMM_PPOOL_USED_BLOCK_HEADER UsedBlock = address_to_block(Block);
-   ULONG UsedSize = UsedBlock->Size;
-   PMM_PPOOL_FREE_BLOCK_HEADER FreeBlock =
-      (PMM_PPOOL_FREE_BLOCK_HEADER)UsedBlock;
-   PMM_PPOOL_FREE_BLOCK_HEADER NextBlock;
-   PMM_PPOOL_FREE_BLOCK_HEADER NextNextBlock;
-
-#if MM_PPOOL_REDZONE_BYTES
-   // write out buffer-overrun detection bytes
-   {
-      int i;
-      PUCHAR Addr = (PUCHAR)Block;
-      //DbgPrint ( "checking buffer-overrun detection bytes..." );
-      for ( i = 0; i < MM_PPOOL_REDZONE_BYTES; i++ )
-      {
-         if (*(Addr-i-1) != MM_PPOOL_REDZONE_VALUE)
-         {
-            DPRINT1("Attempt to free memory %#08x. Redzone underrun!\n", Block);
-         }
-         if (*(Addr+UsedBlock->UserSize+i) != MM_PPOOL_REDZONE_VALUE)
-         {
-            DPRINT1("Attempt to free memory %#08x. Redzone overrun!\n", Block);
-         }
-
-         assert ( *(Addr-i-1) == MM_PPOOL_REDZONE_VALUE );
-         assert ( *(Addr+UsedBlock->UserSize+i) == MM_PPOOL_REDZONE_VALUE );
-      }
-      //DbgPrint ( "done!\n" );
-   }
-#endif//MM_PPOOL_REDZONE_BYTES
-
-   ExAcquireFastMutex(&MmPagedPoolLock);
-
-#if MM_PPOOL_REDZONE_BYTES
-   // remove from used list...
-   {
-      PMM_PPOOL_USED_BLOCK_HEADER pPrev = MmPagedPoolFirstUsedBlock;
-      if ( pPrev == UsedBlock )
-      {
-         // special-case, our freeing block is first in list...
-         MmPagedPoolFirstUsedBlock = pPrev->NextUsed;
-      }
-      else
-      {
-         while ( pPrev && pPrev->NextUsed != UsedBlock )
-            pPrev = pPrev->NextUsed;
-         // if this assert fails - memory has been corrupted
-         // ( or I have a logic error...! )
-         assert ( pPrev->NextUsed == UsedBlock );
-         pPrev->NextUsed = UsedBlock->NextUsed;
-      }
-   }
-#endif//MM_PPOOL_REDZONE_BYTES
-
-   /*
-    * Begin setting up the newly freed block's header.
-    */
-   FreeBlock->Size = UsedSize;
-   ASSERT_SIZE ( FreeBlock->Size );
-
-   /*
-    * Find the blocks immediately before and after the newly freed block on the free list.
-    */
-   PreviousBlock = NULL;
-   NextBlock = MmPagedPoolFirstFreeBlock;
-   while (NextBlock != NULL && NextBlock < FreeBlock)
-   {
-      PreviousBlock = NextBlock;
-      NextBlock = NextBlock->NextFree;
-   }
-
-   /*
-    * Insert the freed block on the free list.
-    */
-   if (PreviousBlock == NULL)
-   {
-      FreeBlock->NextFree = MmPagedPoolFirstFreeBlock;
-      MmPagedPoolFirstFreeBlock = FreeBlock;
-   }
-   else
-   {
-      PreviousBlock->NextFree = FreeBlock;
-      FreeBlock->NextFree = NextBlock;
-   }
-
-   /*
-    * If the next block is immediately adjacent to the newly freed one then
-    * merge them.
-    */
-   if (NextBlock != NULL &&
-         ((char*)FreeBlock + FreeBlock->Size) == (char*)NextBlock)
-   {
-      FreeBlock->Size = FreeBlock->Size + NextBlock->Size;
-      ASSERT_SIZE ( FreeBlock->Size );
-      FreeBlock->NextFree = NextBlock->NextFree;
-      NextNextBlock = NextBlock->NextFree;
-   }
-   else
-   {
-      NextNextBlock = NextBlock;
-   }
-
-   /*
-    * If the previous block is adjacent to the newly freed one then
-    * merge them.
-    */
-   if (PreviousBlock != NULL &&
-         ((char*)PreviousBlock + PreviousBlock->Size) == (char*)FreeBlock)
-   {
-      PreviousBlock->Size = PreviousBlock->Size + FreeBlock->Size;
-      ASSERT_SIZE ( PreviousBlock->Size );
-      PreviousBlock->NextFree = NextNextBlock;
-   }
-
-   VerifyPagedPool();
-
-   ExReleaseFastMutex(&MmPagedPoolLock);
+       ASSERT_IRQL(APC_LEVEL);
+       RPoolFree ( MmPagedPool, Block );
+}
+
+VOID STDCALL
+ExRosDumpPagedPoolByTag ( ULONG Tag )
+{
+       // TODO FIXME - should we ASSERT_IRQL?
+       RPoolDumpByTag ( MmPagedPool, Tag );
+}
+
+ULONG STDCALL
+ExRosQueryPagedPoolTag ( PVOID Addr )
+{
+       // TODO FIXME - should we ASSERT_IRQL?
+       return RPoolQueryTag ( Addr );
+}
+
+#ifdef PPOOL_UMODE_TEST
+
+PVOID TestAlloc ( ULONG Bytes )
+{
+       PVOID ret;
+
+       //printf ( "Allocating block: " ); RPoolStats ( MmPagedPool );
+       //RPoolRedZoneCheck ( MmPagedPool, __FILE__, __LINE__ );
+
+       ret = ExAllocatePagedPoolWithTag ( PagedPool, Bytes, 0 );
+
+       //printf ( "Block %x allocated: ", ret ); RPoolStats ( MmPagedPool );
+       //RPoolRedZoneCheck ( MmPagedPool, __FILE__, __LINE__ );
+
+       return ret;
+}
+
+void TestFree ( PVOID ptr )
+{
+       //printf ( "Freeing block %x: ", ptr ); RPoolStats ( MmPagedPool );
+       //RPoolRedZoneCheck ( MmPagedPool, __FILE__, __LINE__ );
+       ExFreePagedPool(ptr);
+       //printf ( "Block %x freed: ", ptr ); RPoolStats ( MmPagedPool );
+       //RPoolRedZoneCheck ( MmPagedPool, __FILE__, __LINE__ );
+}
+
+int main()
+{
+#define COUNT 100
+       int i, j;
+       char* keepers[COUNT];
+       char* trash[COUNT];
+       int AllocSize[] = { 15, 31, 63, 127, 255, 511, 1023, 2047 };
+       const int ALLOCS = sizeof(AllocSize) / sizeof(0[AllocSize]);
+       DWORD dwStart;
+
+       MmPagedPoolSize = 1*1024*1024;
+       MmPagedPoolBase = malloc ( MmPagedPoolSize );
+       MmInitializePagedPool();
+
+       dwStart = GetTickCount();
+
+       printf ( "test #1 phase #1\n" );
+       for ( i = 0; i < COUNT; i++ )
+       {
+               //printf ( "keeper %i) ", i );
+               keepers[i] = TestAlloc ( AllocSize[i%ALLOCS] );
+               if ( !keepers[i] ) printf ( "allocation failed\n" );
+               //printf ( "trash %i) ", i );
+               trash[i] = TestAlloc ( AllocSize[i%ALLOCS] );
+               if ( !trash[i] ) printf ( "allocation failed\n" );
+       }
+
+       printf ( "test #1 phase #2\n" );
+       for ( i = 0; i < COUNT; i++ )
+       {
+               if ( i == 6 )
+                       i = i;
+               //printf ( "%i) ", i );
+               TestFree ( trash[i] );
+       }
+
+       printf ( "test #1 phase #3\n" );
+       for ( i = 0; i < 4; i++ )
+       {
+               //printf ( "%i) ", i );
+               keepers[i] = TestAlloc ( 4096 );
+               if ( !keepers[i] ) printf ( "allocation failed\n" );
+       }
+
+       printf ( "test #1 phase #4\n" );
+       for ( i = 0; i < 4; i++ )
+       {
+               //printf ( "%i) ", i );
+               TestFree ( keepers[i] );
+       }
+
+       printf ( "test #1 phase #5\n" );
+       srand(1);
+       for ( i = 0; i < COUNT; i++ )
+       {
+               //printf ( "%i) ", i );
+               trash[i] = TestAlloc ( rand()%1024+1 );
+               if ( !trash[i] ) printf ( "allocation failed\n" );
+       }
+       printf ( "test #1 phase #6\n" );
+       for ( i = 0; i < 10000; i++ )
+       {
+               TestFree ( trash[i%COUNT] );
+               trash[i%COUNT] = TestAlloc ( rand()%1024+1 );
+               if ( !trash[i%COUNT] ) printf ( "allocation failed\n" );
+       }
+       printf ( "test #1 phase #7\n" );
+       j = 0;
+       for ( i = 0; i < COUNT; i++ )
+       {
+               if ( trash[i] )
+               {
+                       TestFree ( trash[i] );
+                       ++j;
+               }
+       }
+       printf ( "test #1 phase #8 ( freed %i of %i trash )\n", j, COUNT );
+       if ( !TestAlloc ( 2048 ) )
+               printf ( "Couldn't allocate 2048 bytes after freeing up a whole bunch of blocks\n" );
+
+       free ( MmPagedPoolBase );
+
+       printf ( "test time: %lu\n", GetTickCount() - dwStart );
+
+       printf ( "test #2\n" );
+
+       MmPagedPoolSize = 1024;
+       MmPagedPoolBase = malloc ( MmPagedPoolSize );
+       MmInitializePagedPool();
+
+       TestAlloc ( 512 );
+       i = RPoolLargestAllocPossible ( MmPagedPool, 0 );
+       if ( !TestAlloc ( i ) )
+       {
+               printf ( "allocating last available block failed\n" );
+       }
+
+       free ( MmPagedPoolBase );
+
+       printf ( "done!\n" );
+       return 0;
 }
+#endif//PPOOL_UMODE_TEST
 
 /* EOF */