* Sync up to trunk HEAD (r62502).
[reactos.git] / ntoskrnl / mm / ARM3 / expool.c
index 73a4a81..4558bfa 100644 (file)
@@ -281,6 +281,86 @@ ExpCheckPoolHeader(IN PPOOL_HEADER Entry)
     }
 }
 
+VOID
+NTAPI
+ExpCheckPoolAllocation(
+    PVOID P,
+    POOL_TYPE PoolType,
+    ULONG Tag)
+{
+    PPOOL_HEADER Entry;
+    ULONG i;
+    KIRQL OldIrql;
+    POOL_TYPE RealPoolType;
+
+    /* Get the pool header */
+    Entry = ((PPOOL_HEADER)P) - 1;
+
+    /* Check if this is a large allocation */
+    if (PAGE_ALIGN(P) == P)
+    {
+        /* Lock the pool table */
+        KeAcquireSpinLock(&ExpLargePoolTableLock, &OldIrql);
+
+        /* Find the pool tag */
+        for (i = 0; i < PoolBigPageTableSize; i++)
+        {
+            /* Check if this is our allocation */
+            if (PoolBigPageTable[i].Va == P)
+            {
+                /* Make sure the tag is ok */
+                if (PoolBigPageTable[i].Key != Tag)
+                {
+                    KeBugCheckEx(BAD_POOL_CALLER, 0x0A, (ULONG_PTR)P, PoolBigPageTable[i].Key, Tag);
+                }
+
+                break;
+            }
+        }
+
+        /* Release the lock */
+        KeReleaseSpinLock(&ExpLargePoolTableLock, OldIrql);
+
+        if (i == PoolBigPageTableSize)
+        {
+            /* Did not find the allocation */
+            //ASSERT(FALSE);
+        }
+
+        /* Get Pool type by address */
+        RealPoolType = MmDeterminePoolType(P);
+    }
+    else
+    {
+        /* Verify the tag */
+        if (Entry->PoolTag != Tag)
+        {
+            DPRINT1("Allocation has wrong pool tag! Expected '%.4s', got '%.4s' (0x%08lx)\n",
+                    &Tag, &Entry->PoolTag, Entry->PoolTag);
+            KeBugCheckEx(BAD_POOL_CALLER, 0x0A, (ULONG_PTR)P, Entry->PoolTag, Tag);
+        }
+
+        /* Check the rest of the header */
+        ExpCheckPoolHeader(Entry);
+
+        /* Get Pool type from entry */
+        RealPoolType = (Entry->PoolType - 1);
+    }
+
+    /* Should we check the pool type? */
+    if (PoolType != -1)
+    {
+        /* Verify the pool type */
+        if (RealPoolType != PoolType)
+        {
+            DPRINT1("Wrong pool type! Expected %s, got %s\n",
+                    PoolType & BASE_POOL_TYPE_MASK ? "PagedPool" : "NonPagedPool",
+                    (Entry->PoolType - 1) & BASE_POOL_TYPE_MASK ? "PagedPool" : "NonPagedPool");
+            KeBugCheckEx(BAD_POOL_CALLER, 0xCC, (ULONG_PTR)P, Entry->PoolTag, Tag);
+        }
+    }
+}
+
 VOID
 NTAPI
 ExpCheckPoolBlocks(IN PVOID Block)
@@ -535,6 +615,7 @@ ExpRemovePoolTracker(IN ULONG Key,
     Table = PoolTrackTable;
     TableMask = PoolTrackTableMask;
     TableSize = PoolTrackTableSize;
+    DBG_UNREFERENCED_LOCAL_VARIABLE(TableSize);
 
     //
     // Compute the hash for this key, and loop all the possible buckets
@@ -572,8 +653,8 @@ ExpRemovePoolTracker(IN ULONG Key,
         //
         if (!TableEntry->Key)
         {
-            DPRINT1("Empty item reached in tracker table. Tag=0x%08lx, NumberOfBytes=%lu, PoolType=%d\n",
-                    Key, (ULONG)NumberOfBytes, PoolType);
+            DPRINT1("Empty item reached in tracker table. Hash=0x%lx, TableMask=0x%lx, Tag=0x%08lx, NumberOfBytes=%lu, PoolType=%d\n",
+                    Hash, TableMask, Key, (ULONG)NumberOfBytes, PoolType);
             ASSERT(Hash == TableMask);
         }
 
@@ -637,6 +718,7 @@ ExpInsertPoolTracker(IN ULONG Key,
     Table = PoolTrackTable;
     TableMask = PoolTrackTableMask;
     TableSize = PoolTrackTableSize;
+    DBG_UNREFERENCED_LOCAL_VARIABLE(TableSize);
 
     //
     // Compute the hash for this key, and loop all the possible buckets
@@ -926,9 +1008,9 @@ InitializePool(IN POOL_TYPE PoolType,
         //
         // During development, print this out so we can see what's happening
         //
-        DPRINT1("EXPOOL: Pool Tracker Table at: 0x%p with 0x%lx bytes\n",
+        DPRINT("EXPOOL: Pool Tracker Table at: 0x%p with 0x%lx bytes\n",
                 PoolTrackTable, PoolTrackTableSize * sizeof(POOL_TRACKER_TABLE));
-        DPRINT1("EXPOOL: Big Pool Tracker Table at: 0x%p with 0x%lx bytes\n",
+        DPRINT("EXPOOL: Big Pool Tracker Table at: 0x%p with 0x%lx bytes\n",
                 PoolBigPageTable, PoolBigPageTableSize * sizeof(POOL_TRACKER_BIG_PAGES));
 
         //
@@ -1254,7 +1336,7 @@ ExpAddTagForBigPages(IN PVOID Va,
             InterlockedIncrementUL(&ExpPoolBigEntriesInUse);
             if ((i >= 16) && (ExpPoolBigEntriesInUse > (TableSize / 4)))
             {
-                DPRINT1("Should attempt expansion since we now have %lu entries\n",
+                DPRINT("Should attempt expansion since we now have %lu entries\n",
                         ExpPoolBigEntriesInUse);
             }
 
@@ -1531,7 +1613,7 @@ ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
             //
             if (ExpPoolFlags & POOL_FLAG_DBGPRINT_ON_FAILURE)
             {
-                DPRINT1("EX: ExAllocatePool (%p, 0x%x) returning NULL\n",
+                DPRINT1("EX: ExAllocatePool (%lu, 0x%x) returning NULL\n",
                         NumberOfBytes,
                         OriginalType);
                 if (ExpPoolFlags & POOL_FLAG_CRASH_ON_FAILURE) DbgBreakPoint();
@@ -1593,7 +1675,7 @@ ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
     //
     // Handle lookaside list optimization for both paged and nonpaged pool
     //
-    if (i <= MAXIMUM_PROCESSORS)
+    if (i <= NUMBER_POOL_LOOKASIDE_LISTS)
     {
         //
         // Try popping it from the per-CPU lookaside list
@@ -1626,7 +1708,7 @@ ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
             // Get the real entry, write down its pool type, and track it
             //
             Entry--;
-            Entry->PoolType = PoolType + 1;
+            Entry->PoolType = OriginalType + 1;
             ExpInsertPoolTracker(Tag,
                                  Entry->BlockSize * POOL_BLOCK_SIZE,
                                  OriginalType);
@@ -1798,7 +1880,7 @@ ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
             // We have found an entry for this allocation, so set the pool type
             // and release the lock since we're done
             //
-            Entry->PoolType = PoolType + 1;
+            Entry->PoolType = OriginalType + 1;
             ExpCheckPoolBlocks(Entry);
             ExUnlockPool(PoolDesc, OldIrql);
 
@@ -1828,7 +1910,7 @@ ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
     //
     // There were no free entries left, so we have to allocate a new fresh page
     //
-    Entry = MiAllocatePoolPages(PoolType, PAGE_SIZE);
+    Entry = MiAllocatePoolPages(OriginalType, PAGE_SIZE);
     if (!Entry)
     {
         //
@@ -1855,7 +1937,7 @@ ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
         //
         if (ExpPoolFlags & POOL_FLAG_DBGPRINT_ON_FAILURE)
         {
-            DPRINT1("EX: ExAllocatePool (%p, 0x%x) returning NULL\n",
+            DPRINT1("EX: ExAllocatePool (%lu, 0x%x) returning NULL\n",
                     NumberOfBytes,
                     OriginalType);
             if (ExpPoolFlags & POOL_FLAG_CRASH_ON_FAILURE) DbgBreakPoint();
@@ -1881,7 +1963,7 @@ ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
     //
     Entry->Ulong1 = 0;
     Entry->BlockSize = i;
-    Entry->PoolType = PoolType + 1;
+    Entry->PoolType = OriginalType + 1;
 
     //
     // This page will have two entries -- one for the allocation (which we just
@@ -1941,7 +2023,7 @@ ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
     InterlockedIncrement((PLONG)&PoolDesc->RunningAllocs);
     ExpInsertPoolTracker(Tag,
                          Entry->BlockSize * POOL_BLOCK_SIZE,
-                         PoolType);
+                         OriginalType);
 
     //
     // And return the pool allocation
@@ -1983,6 +2065,7 @@ ExFreePoolWithTag(IN PVOID P,
     PFN_NUMBER PageCount, RealPageCount;
     PKPRCB Prcb = KeGetCurrentPrcb();
     PGENERAL_LOOKASIDE LookasideList;
+    PEPROCESS Process;
 
     //
     // Check if any of the debug flags are enabled
@@ -2173,10 +2256,34 @@ ExFreePoolWithTag(IN PVOID P,
                          BlockSize * POOL_BLOCK_SIZE,
                          Entry->PoolType - 1);
 
+    //
+    // Release pool quota, if any
+    //
+    if ((Entry->PoolType - 1) & QUOTA_POOL_MASK)
+    {
+        Process = ((PVOID *)POOL_NEXT_BLOCK(Entry))[-1];
+        ASSERT(Process != NULL);
+        if (Process)
+        {
+            if (Process->Pcb.Header.Type != ProcessObject)
+            {
+                DPRINT1("Object %p is not a process. Type %u, pool type 0x%x, block size %u\n",
+                        Process, Process->Pcb.Header.Type, Entry->PoolType, BlockSize);
+                KeBugCheckEx(BAD_POOL_CALLER,
+                             0x0D,
+                             (ULONG_PTR)P,
+                             Tag,
+                             (ULONG_PTR)Process);
+            }
+            PsReturnPoolQuota(Process, PoolType, BlockSize * POOL_BLOCK_SIZE);
+            ObDereferenceObject(Process);
+        }
+    }
+
     //
     // Is this allocation small enough to have come from a lookaside list?
     //
-    if (BlockSize <= MAXIMUM_PROCESSORS)
+    if (BlockSize <= NUMBER_POOL_LOOKASIDE_LISTS)
     {
         //
         // Try pushing it into the per-CPU lookaside list
@@ -2429,11 +2536,213 @@ ExAllocatePoolWithQuotaTag(IN POOL_TYPE PoolType,
                            IN SIZE_T NumberOfBytes,
                            IN ULONG Tag)
 {
+    BOOLEAN Raise = TRUE;
+    PVOID Buffer;
+    PPOOL_HEADER Entry;
+    NTSTATUS Status;
+    PEPROCESS Process = PsGetCurrentProcess();
+
     //
-    // Allocate the pool
+    // Check if we should fail instead of raising an exception
     //
-    UNIMPLEMENTED;
-    return ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag);
+    if (PoolType & POOL_QUOTA_FAIL_INSTEAD_OF_RAISE)
+    {
+        Raise = FALSE;
+        PoolType &= ~POOL_QUOTA_FAIL_INSTEAD_OF_RAISE;
+    }
+
+    //
+    // Inject the pool quota mask
+    //
+    PoolType += QUOTA_POOL_MASK;
+
+    //
+    // Check if we have enough space to add the quota owner process, as long as
+    // this isn't the system process, which never gets charged quota
+    //
+    ASSERT(NumberOfBytes != 0);
+    if ((NumberOfBytes <= (PAGE_SIZE - POOL_BLOCK_SIZE - sizeof(PVOID))) &&
+        (Process != PsInitialSystemProcess))
+    {
+        //
+        // Add space for our EPROCESS pointer
+        //
+        NumberOfBytes += sizeof(PEPROCESS);
+    }
+    else
+    {
+        //
+        // We won't be able to store the pointer, so don't use quota for this
+        //
+        PoolType -= QUOTA_POOL_MASK;
+    }
+
+    //
+    // Allocate the pool buffer now
+    //
+    Buffer = ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag);
+
+    //
+    // If the buffer is page-aligned, this is a large page allocation and we
+    // won't touch it
+    //
+    if (PAGE_ALIGN(Buffer) != Buffer)
+    {
+        //
+        // Also if special pool is enabled, and this was allocated from there,
+        // we won't touch it either
+        //
+        if ((ExpPoolFlags & POOL_FLAG_SPECIAL_POOL) &&
+            (MmIsSpecialPoolAddress(Buffer)))
+        {
+            return Buffer;
+        }
+
+        //
+        // If it wasn't actually allocated with quota charges, ignore it too
+        //
+        if (!(PoolType & QUOTA_POOL_MASK)) return Buffer;
+
+        //
+        // If this is the system process, we don't charge quota, so ignore
+        //
+        if (Process == PsInitialSystemProcess) return Buffer;
+
+        //
+        // Actually go and charge quota for the process now
+        //
+        Entry = POOL_ENTRY(Buffer);
+        Status = PsChargeProcessPoolQuota(Process,
+                                          PoolType & BASE_POOL_TYPE_MASK,
+                                          Entry->BlockSize * POOL_BLOCK_SIZE);
+        if (!NT_SUCCESS(Status))
+        {
+            //
+            // Quota failed, back out the allocation, clear the owner, and fail
+            //
+            ((PVOID *)POOL_NEXT_BLOCK(Entry))[-1] = NULL;
+            ExFreePoolWithTag(Buffer, Tag);
+            if (Raise) RtlRaiseStatus(Status);
+            return NULL;
+        }
+
+        //
+        // Quota worked, write the owner and then reference it before returning
+        //
+        ((PVOID *)POOL_NEXT_BLOCK(Entry))[-1] = Process;
+        ObReferenceObject(Process);
+    }
+    else if (!(Buffer) && (Raise))
+    {
+        //
+        // The allocation failed, raise an error if we are in raise mode
+        //
+        RtlRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
+    }
+
+    //
+    // Return the allocated buffer
+    //
+    return Buffer;
 }
 
+#if DBG && 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;
+}
+
+#endif // DBG && KDBG
+
 /* EOF */