completely rewritten PagedPool.
[reactos.git] / reactos / ntoskrnl / mm / ppool.c
index 17f6055..ef93d52 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: ppool.c,v 1.4 2001/12/20 03:56:09 dwelch 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
@@ -7,53 +7,64 @@
  * 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 *******************************************************************/
+#undef ASSERT
+#define ASSERT(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d\n", __FILE__,__LINE__); KeBugCheck(0); }
 
-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;
+// 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
 
-typedef struct _MM_PPOOL_USED_BLOCK_HEADER
-{
-  ULONG Size;
-} MM_PPOOL_USED_BLOCK_HEADER, *PMM_PPOOL_USED_BLOCK_HEADER;
+#include "RPoolMgr.h"
+
+/* GLOBALS *******************************************************************/
 
 PVOID MmPagedPoolBase;
 ULONG MmPagedPoolSize;
-static FAST_MUTEX MmPagedPoolLock;
-static PMM_PPOOL_FREE_BLOCK_HEADER MmPagedPoolFirstFreeBlock;
+ULONG MmTotalPagedPoolQuota = 0; // TODO FIXME commented out until we use it
+static PR_POOL MmPagedPool = NULL;
 
 /* FUNCTIONS *****************************************************************/
 
-VOID MmInitializePagedPool(VOID)
+VOID INIT_FUNCTION
+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);
-  MmPagedPoolFirstFreeBlock->Size = MmPagedPoolSize;
-  MmPagedPoolFirstFreeBlock->NextFree = NULL;
-
-  ExInitializeFastMutex(&MmPagedPoolLock);
+       /*
+        * 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);
 }
 
 /**********************************************************************
- * NAME                                                        INTERNAL
- *     ExAllocatePagedPoolWithTag@12
+ * NAME       INTERNAL
+ * ExAllocatePagedPoolWithTag@12
  *
  * DESCRIPTION
  *
@@ -62,199 +73,173 @@ VOID MmInitializePagedPool(VOID)
  * RETURN VALUE
  */
 PVOID STDCALL
-ExAllocatePagedPoolWithTag (IN POOL_TYPE       PoolType,
-                           IN  ULONG           NumberOfBytes,
-                           IN  ULONG           Tag)
+ExAllocatePagedPoolWithTag (IN POOL_TYPE PoolType,
+                            IN ULONG  NumberOfBytes,
+                            IN ULONG  Tag)
+{
+       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)
+{
+       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()
 {
-  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;
-
-  /*
-   * Don't bother allocating anything for a zero-byte block.
-   */
-  if (NumberOfBytes == 0)
-    {
-      return(NULL);
-    }
-
-  /*
-   * Calculate the total number of bytes we will need.
-   */
-  BlockSize = NumberOfBytes + sizeof(MM_PPOOL_USED_BLOCK_HEADER);
-
-  ExAcquireFastMutex(&MmPagedPoolLock);
-
-  /*
-   * Find the best fitting block.
-   */
-  PreviousBlock = NULL;
-  BestPreviousBlock = BestBlock = NULL;
-  CurrentBlock = MmPagedPoolFirstFreeBlock;
-  while (CurrentBlock != NULL)
-    {
-      if (CurrentBlock->Size >= BlockSize &&
-         (BestBlock == NULL || 
-          (BestBlock->Size - BlockSize) > (CurrentBlock->Size - BlockSize)))
+#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++ )
        {
-         BestPreviousBlock = PreviousBlock;
-         BestBlock = CurrentBlock;
+               if ( i == 6 )
+                       i = i;
+               //printf ( "%i) ", i );
+               TestFree ( trash[i] );
        }
 
-      PreviousBlock = CurrentBlock;
-      CurrentBlock = CurrentBlock->NextFree;
-    }
-
-  /*
-   * We didn't find anything suitable at all.
-   */
-  if (BestBlock == NULL)
-    {
-      ExReleaseFastMutex(&MmPagedPoolLock);
-      return(NULL);
-    }
-
-  /*
-   * Is there enough space to create a second block from the unused portion.
-   */
-  if ((BestBlock->Size - BlockSize) > sizeof(PMM_PPOOL_USED_BLOCK_HEADER))
-    {
-      ULONG NewSize = BestBlock->Size - BlockSize;
-
-      /*
-       * Create the new free block.
-       */
-      NextBlock = (PMM_PPOOL_FREE_BLOCK_HEADER)((PVOID)BestBlock + BlockSize);
-      NextBlock->Size = NewSize;
-      NextBlock->NextFree = BestBlock->NextFree;
-
-      /*
-       * Replace the old free block with it.
-       */
-      if (BestPreviousBlock == NULL)
+       printf ( "test #1 phase #3\n" );
+       for ( i = 0; i < 4; i++ )
        {
-         MmPagedPoolFirstFreeBlock = NextBlock;
+               //printf ( "%i) ", i );
+               keepers[i] = TestAlloc ( 4096 );
+               if ( !keepers[i] ) printf ( "allocation failed\n" );
        }
-      else
+
+       printf ( "test #1 phase #4\n" );
+       for ( i = 0; i < 4; i++ )
        {
-         BestPreviousBlock->NextFree = NextBlock;
+               //printf ( "%i) ", i );
+               TestFree ( keepers[i] );
        }
 
-      /*
-       * Create the new used block header.
-       */
-      NewBlock = (PMM_PPOOL_USED_BLOCK_HEADER)BestBlock;
-      NewBlock->Size = BlockSize;
-    }
-  else
-    {
-      ULONG NewSize = BestBlock->Size;
-
-      /*
-       * Remove the selected block from the list of free blocks.
-       */
-      if (BestPreviousBlock == NULL)
+       printf ( "test #1 phase #5\n" );
+       srand(1);
+       for ( i = 0; i < COUNT; i++ )
        {
-         MmPagedPoolFirstFreeBlock = BestBlock->NextFree;
+               //printf ( "%i) ", i );
+               trash[i] = TestAlloc ( rand()%1024+1 );
+               if ( !trash[i] ) printf ( "allocation failed\n" );
        }
-      else
+       printf ( "test #1 phase #6\n" );
+       for ( i = 0; i < 10000; i++ )
        {
-         BestPreviousBlock->NextFree = BestBlock->NextFree;
+               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" );
 
-      /*
-       * Set up the header of the new block
-       */
-      NewBlock = (PMM_PPOOL_USED_BLOCK_HEADER)BestBlock;
-      NewBlock->Size = NewSize;
-    }
+       free ( MmPagedPoolBase );
 
-  ExReleaseFastMutex(&MmPagedPoolLock);
+       printf ( "test time: %lu\n", GetTickCount() - dwStart );
 
-  BlockAddress = (PVOID)NewBlock + sizeof(MM_PPOOL_USED_BLOCK_HEADER);
+       printf ( "test #2\n" );
 
-  memset(BlockAddress, 0, NumberOfBytes);
+       MmPagedPoolSize = 1024;
+       MmPagedPoolBase = malloc ( MmPagedPoolSize );
+       MmInitializePagedPool();
 
-  return(BlockAddress);
-}
+       TestAlloc ( 512 );
+       i = RPoolLargestAllocPossible ( MmPagedPool, 0 );
+       if ( !TestAlloc ( i ) )
+       {
+               printf ( "allocating last available block failed\n" );
+       }
 
-VOID STDCALL
-ExFreePagedPool(IN PVOID Block)
-{
-  PMM_PPOOL_FREE_BLOCK_HEADER PreviousBlock;
-  PMM_PPOOL_USED_BLOCK_HEADER UsedBlock = 
-    (PMM_PPOOL_USED_BLOCK_HEADER)(Block - sizeof(MM_PPOOL_USED_BLOCK_HEADER));
-  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;
-
-  ExAcquireFastMutex(&MmPagedPoolLock);
-
-  /*
-   * Begin setting up the new free block's header.
-   */
-  FreeBlock->Size = UsedSize;
-
-  /*
-   * Find the block immediate before and after it 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
-   * free them.
-   */
-  if (NextBlock != NULL && 
-      ((PVOID)FreeBlock + FreeBlock->Size) == (PVOID)NextBlock)
-    {
-      FreeBlock->Size = FreeBlock->Size + NextBlock->Size;
-      FreeBlock->NextFree = NextBlock->NextFree;
-      NextNextBlock = NextBlock->NextFree;
-    }
-  else
-    {
-      NextNextBlock = NextBlock;
-    }
-
-  /*
-   * If the previous block is adjacent to the newly freed one then
-   * purge them.
-   */
-  if (PreviousBlock != NULL && 
-      ((PVOID)PreviousBlock + PreviousBlock->Size) == (PVOID)FreeBlock)
-    {
-      PreviousBlock->Size = PreviousBlock->Size + FreeBlock->Size;
-      PreviousBlock->NextFree = NextNextBlock;
-    }
-
-  ExReleaseFastMutex(&MmPagedPoolLock);
+       free ( MmPagedPoolBase );
+
+       printf ( "done!\n" );
+       return 0;
 }
+#endif//PPOOL_UMODE_TEST
 
 /* EOF */