2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/expool.c
5 * PURPOSE: ARM Memory Manager Executive Pool Manager
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 #line 15 "ARMĀ³::EXPOOL"
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
19 #undef ExAllocatePoolWithQuota
20 #undef ExAllocatePoolWithQuotaTag
22 /* GLOBALS ********************************************************************/
24 ULONG ExpNumberOfPagedPools
;
25 POOL_DESCRIPTOR NonPagedPoolDescriptor
;
26 PPOOL_DESCRIPTOR ExpPagedPoolDescriptor
[16 + 1];
27 PPOOL_DESCRIPTOR PoolVector
[2];
29 PKGUARDED_MUTEX ExpPagedPoolMutex
;
31 /* PRIVATE FUNCTIONS **********************************************************/
35 ExInitializePoolDescriptor(IN PPOOL_DESCRIPTOR PoolDescriptor
,
36 IN POOL_TYPE PoolType
,
41 PLIST_ENTRY NextEntry
, LastEntry
;
44 // Setup the descriptor based on the caller's request
46 PoolDescriptor
->PoolType
= PoolType
;
47 PoolDescriptor
->PoolIndex
= PoolIndex
;
48 PoolDescriptor
->Threshold
= Threshold
;
49 PoolDescriptor
->LockAddress
= PoolLock
;
52 // Initialize accounting data
54 PoolDescriptor
->RunningAllocs
= 0;
55 PoolDescriptor
->RunningDeAllocs
= 0;
56 PoolDescriptor
->TotalPages
= 0;
57 PoolDescriptor
->TotalBytes
= 0;
58 PoolDescriptor
->TotalBigPages
= 0;
61 // Nothing pending for now
63 PoolDescriptor
->PendingFrees
= NULL
;
64 PoolDescriptor
->PendingFreeDepth
= 0;
67 // Loop all the descriptor's allocation lists and initialize them
69 NextEntry
= PoolDescriptor
->ListHeads
;
70 LastEntry
= NextEntry
+ POOL_LISTS_PER_PAGE
;
71 while (NextEntry
< LastEntry
) InitializeListHead(NextEntry
++);
76 InitializePool(IN POOL_TYPE PoolType
,
79 PPOOL_DESCRIPTOR Descriptor
;
82 // Check what kind of pool this is
84 if (PoolType
== NonPagedPool
)
87 // Initialize the nonpaged pool descriptor
89 PoolVector
[NonPagedPool
] = &NonPagedPoolDescriptor
;
90 ExInitializePoolDescriptor(PoolVector
[NonPagedPool
],
99 // Allocate the pool descriptor
101 Descriptor
= ExAllocatePoolWithTag(NonPagedPool
,
102 sizeof(KGUARDED_MUTEX
) +
103 sizeof(POOL_DESCRIPTOR
),
108 // This is really bad...
110 KeBugCheckEx(MUST_SUCCEED_POOL_EMPTY
,
118 // Setup the vector and guarded mutex for paged pool
120 PoolVector
[PagedPool
] = Descriptor
;
121 ExpPagedPoolMutex
= (PKGUARDED_MUTEX
)(Descriptor
+ 1);
122 KeInitializeGuardedMutex(ExpPagedPoolMutex
);
123 ExInitializePoolDescriptor(Descriptor
,
133 ExLockPool(IN PPOOL_DESCRIPTOR Descriptor
)
136 // Check if this is nonpaged pool
138 if ((Descriptor
->PoolType
& BASE_POOL_TYPE_MASK
) == NonPagedPool
)
141 // Use the queued spin lock
143 return KeAcquireQueuedSpinLock(LockQueueNonPagedPoolLock
);
148 // Use the guarded mutex
150 KeAcquireGuardedMutex(Descriptor
->LockAddress
);
157 ExUnlockPool(IN PPOOL_DESCRIPTOR Descriptor
,
161 // Check if this is nonpaged pool
163 if ((Descriptor
->PoolType
& BASE_POOL_TYPE_MASK
) == NonPagedPool
)
166 // Use the queued spin lock
168 KeReleaseQueuedSpinLock(LockQueueNonPagedPoolLock
, OldIrql
);
173 // Use the guarded mutex
175 KeReleaseGuardedMutex(Descriptor
->LockAddress
);
179 /* PUBLIC FUNCTIONS ***********************************************************/
186 ExAllocatePoolWithTag(IN POOL_TYPE PoolType
,
187 IN SIZE_T NumberOfBytes
,
190 PPOOL_DESCRIPTOR PoolDesc
;
191 PLIST_ENTRY ListHead
;
192 PPOOL_HEADER Entry
, NextEntry
, FragmentEntry
;
197 // Check for paged pool
199 if (PoolType
== PagedPool
) return ExAllocatePagedPoolWithTag(PagedPool
, NumberOfBytes
, Tag
);
202 // Some sanity checks
205 ASSERT(Tag
!= ' GIB');
206 ASSERT(NumberOfBytes
!= 0);
209 // Get the pool type and its corresponding vector for this request
211 PoolType
= PoolType
& BASE_POOL_TYPE_MASK
;
212 PoolDesc
= PoolVector
[PoolType
];
213 ASSERT(PoolDesc
!= NULL
);
216 // Check if this is a big page allocation
218 if (NumberOfBytes
> POOL_MAX_ALLOC
)
221 // Then just return the number of pages requested
223 return MiAllocatePoolPages(PoolType
, NumberOfBytes
);
227 // Should never request 0 bytes from the pool, but since so many drivers do
228 // it, we'll just assume they want 1 byte, based on NT's similar behavior
230 if (!NumberOfBytes
) NumberOfBytes
= 1;
233 // A pool allocation is defined by its data, a linked list to connect it to
234 // the free list (if necessary), and a pool header to store accounting info.
235 // Calculate this size, then convert it into a block size (units of pool
238 // Note that i cannot overflow (past POOL_LISTS_PER_PAGE) because any such
239 // request would've been treated as a POOL_MAX_ALLOC earlier and resulted in
240 // the direct allocation of pages.
242 i
= (NumberOfBytes
+ sizeof(POOL_HEADER
) + sizeof(LIST_ENTRY
) - 1) /
246 // Loop in the free lists looking for a block if this size. Start with the
247 // list optimized for this kind of size lookup
249 ListHead
= &PoolDesc
->ListHeads
[i
];
253 // Are there any free entries available on this list?
255 if (!IsListEmpty(ListHead
))
258 // Acquire the pool lock now
260 OldIrql
= ExLockPool(PoolDesc
);
263 // And make sure the list still has entries
265 if (IsListEmpty(ListHead
))
268 // Someone raced us (and won) before we had a chance to acquire
273 ExUnlockPool(PoolDesc
, OldIrql
);
279 // Remove a free entry from the list
280 // Note that due to the way we insert free blocks into multiple lists
281 // there is a guarantee that any block on this list will either be
282 // of the correct size, or perhaps larger.
284 Entry
= (PPOOL_HEADER
)RemoveHeadList(ListHead
) - 1;
285 ASSERT(Entry
->BlockSize
>= i
);
286 ASSERT(Entry
->PoolType
== 0);
289 // Check if this block is larger that what we need. The block could
290 // not possibly be smaller, due to the reason explained above (and
291 // we would've asserted on a checked build if this was the case).
293 if (Entry
->BlockSize
!= i
)
296 // Is there an entry before this one?
298 if (Entry
->PreviousSize
== 0)
301 // There isn't anyone before us, so take the next block and
302 // turn it into a fragment that contains the leftover data
303 // that we don't need to satisfy the caller's request
305 FragmentEntry
= Entry
+ i
;
306 FragmentEntry
->BlockSize
= Entry
->BlockSize
- i
;
309 // And make it point back to us
311 FragmentEntry
->PreviousSize
= i
;
314 // Now get the block that follows the new fragment and check
315 // if it's still on the same page as us (and not at the end)
317 NextEntry
= FragmentEntry
+ FragmentEntry
->BlockSize
;
318 if (PAGE_ALIGN(NextEntry
) != NextEntry
)
321 // Adjust this next block to point to our newly created
324 NextEntry
->PreviousSize
= FragmentEntry
->BlockSize
;
330 // There is a free entry before us, which we know is smaller
331 // so we'll make this entry the fragment instead
333 FragmentEntry
= Entry
;
336 // And then we'll remove from it the actual size required.
337 // Now the entry is a leftover free fragment
339 Entry
->BlockSize
-= i
;
342 // Now let's go to the next entry after the fragment (which
343 // used to point to our original free entry) and make it
344 // reference the new fragment entry instead.
346 // This is the entry that will actually end up holding the
349 Entry
+= Entry
->BlockSize
;
350 Entry
->PreviousSize
= FragmentEntry
->BlockSize
;
353 // And now let's go to the entry after that one and check if
354 // it's still on the same page, and not at the end
356 NextEntry
= Entry
+ i
;
357 if (PAGE_ALIGN(NextEntry
) != NextEntry
)
360 // Make it reference the allocation entry
362 NextEntry
->PreviousSize
= i
;
367 // Now our (allocation) entry is the right size
369 Entry
->BlockSize
= i
;
372 // And the next entry is now the free fragment which contains
373 // the remaining difference between how big the original entry
374 // was, and the actual size the caller needs/requested.
376 FragmentEntry
->PoolType
= 0;
377 BlockSize
= FragmentEntry
->BlockSize
;
380 // Now check if enough free bytes remained for us to have a
381 // "full" entry, which contains enough bytes for a linked list
382 // and thus can be used for allocations (up to 8 bytes...)
387 // Insert the free entry into the free list for this size
389 InsertTailList(&PoolDesc
->ListHeads
[BlockSize
- 1],
390 (PLIST_ENTRY
)FragmentEntry
+ 1);
395 // We have found an entry for this allocation, so set the pool type
396 // and release the lock since we're done
398 Entry
->PoolType
= PoolType
+ 1;
399 ExUnlockPool(PoolDesc
, OldIrql
);
402 // Return the pool allocation
404 Entry
->PoolTag
= Tag
;
407 } while (++ListHead
!= &PoolDesc
->ListHeads
[POOL_LISTS_PER_PAGE
]);
410 // There were no free entries left, so we have to allocate a new fresh page
412 Entry
= MiAllocatePoolPages(PoolType
, PAGE_SIZE
);
414 Entry
->BlockSize
= i
;
415 Entry
->PoolType
= PoolType
+ 1;
418 // This page will have two entries -- one for the allocation (which we just
419 // created above), and one for the remaining free bytes, which we're about
420 // to create now. The free bytes are the whole page minus what was allocated
421 // and then converted into units of block headers.
423 BlockSize
= (PAGE_SIZE
/ sizeof(POOL_HEADER
)) - i
;
424 FragmentEntry
= Entry
+ i
;
425 FragmentEntry
->Ulong1
= 0;
426 FragmentEntry
->BlockSize
= BlockSize
;
427 FragmentEntry
->PreviousSize
= i
;
430 // Now check if enough free bytes remained for us to have a "full" entry,
431 // which contains enough bytes for a linked list and thus can be used for
432 // allocations (up to 8 bytes...)
434 if (FragmentEntry
->BlockSize
!= 1)
437 // Excellent -- acquire the pool lock
439 OldIrql
= ExLockPool(PoolDesc
);
442 // And insert the free entry into the free list for this block size
444 InsertTailList(&PoolDesc
->ListHeads
[BlockSize
- 1],
445 (PLIST_ENTRY
)FragmentEntry
+ 1);
448 // Release the pool lock
450 ExUnlockPool(PoolDesc
, OldIrql
);
454 // And return the pool allocation
456 Entry
->PoolTag
= Tag
;
465 ExAllocatePool(POOL_TYPE PoolType
,
466 SIZE_T NumberOfBytes
)
469 // Use a default tag of "None"
471 return ExAllocatePoolWithTag(PoolType
, NumberOfBytes
, 'enoN');
479 ExFreePoolWithTag(IN PVOID P
,
482 PPOOL_HEADER Entry
, NextEntry
;
486 PPOOL_DESCRIPTOR PoolDesc
;
487 BOOLEAN Combined
= FALSE
;
490 // Check for paged pool
492 if ((P
>= MmPagedPoolBase
) &&
493 (P
<= (PVOID
)((ULONG_PTR
)MmPagedPoolBase
+ MmPagedPoolSize
)))
503 // Quickly deal with big page allocations
505 if (PAGE_ALIGN(P
) == P
)
512 // Get the entry for this pool allocation
513 // The pointer math here may look wrong or confusing, but it is quite right
519 // Get the size of the entry, and it's pool type, then load the descriptor
520 // for this pool type
522 BlockSize
= Entry
->BlockSize
;
523 PoolType
= (Entry
->PoolType
& 3) - 1;
524 PoolDesc
= PoolVector
[PoolType
];
527 // Get the pointer to the next entry
529 NextEntry
= Entry
+ BlockSize
;
532 // Acquire the pool lock
534 OldIrql
= ExLockPool(PoolDesc
);
537 // Check if the next allocation is at the end of the page
539 if (PAGE_ALIGN(NextEntry
) != NextEntry
)
542 // We may be able to combine the block if it's free
544 if (NextEntry
->PoolType
== 0)
547 // The next block is free, so we'll do a combine
552 // Make sure there's actual data in the block -- anything smaller
553 // than this means we only have the header, so there's no linked list
556 if ((NextEntry
->BlockSize
!= 1))
559 // The block is at least big enough to have a linked list, so go
560 // ahead and remove it
562 RemoveEntryList((PLIST_ENTRY
)NextEntry
+ 1);
566 // Our entry is now combined with the next entry
568 Entry
->BlockSize
= Entry
->BlockSize
+ NextEntry
->BlockSize
;
573 // Now check if there was a previous entry on the same page as us
575 if (Entry
->PreviousSize
)
578 // Great, grab that entry and check if it's free
580 NextEntry
= Entry
- Entry
->PreviousSize
;
581 if (NextEntry
->PoolType
== 0)
584 // It is, so we can do a combine
589 // Make sure there's actual data in the block -- anything smaller
590 // than this means we only have the header so there's no linked list
593 if ((NextEntry
->BlockSize
!= 1))
596 // The block is at least big enough to have a linked list, so go
597 // ahead and remove it
599 RemoveEntryList((PLIST_ENTRY
)NextEntry
+ 1);
603 // Combine our original block (which might've already been combined
604 // with the next block), into the previous block
606 NextEntry
->BlockSize
= NextEntry
->BlockSize
+ Entry
->BlockSize
;
609 // And now we'll work with the previous block instead
616 // By now, it may have been possible for our combined blocks to actually
617 // have made up a full page (if there were only 2-3 allocations on the
618 // page, they could've all been combined).
620 if ((PAGE_ALIGN(Entry
) == Entry
) &&
621 (PAGE_ALIGN(Entry
+ Entry
->BlockSize
) == Entry
+ Entry
->BlockSize
))
624 // In this case, release the pool lock, and free the page
626 ExUnlockPool(PoolDesc
, OldIrql
);
627 MiFreePoolPages(Entry
);
632 // Otherwise, we now have a free block (or a combination of 2 or 3)
635 BlockSize
= Entry
->BlockSize
;
636 ASSERT(BlockSize
!= 1);
639 // Check if we actually did combine it with anyone
644 // Get the first combined block (either our original to begin with, or
645 // the one after the original, depending if we combined with the previous)
647 NextEntry
= Entry
+ BlockSize
;
650 // As long as the next block isn't on a page boundary, have it point
653 if (PAGE_ALIGN(NextEntry
) != NextEntry
) NextEntry
->PreviousSize
= BlockSize
;
657 // Insert this new free block, and release the pool lock
659 InsertHeadList(&PoolDesc
->ListHeads
[BlockSize
- 1], (PLIST_ENTRY
)Entry
+ 1);
660 ExUnlockPool(PoolDesc
, OldIrql
);
671 // Just free without checking for the tag
673 ExFreePoolWithTag(P
, 0);
681 ExQueryPoolBlockSize(IN PVOID PoolBlock
,
682 OUT PBOOLEAN QuotaCharged
)
697 ExAllocatePoolWithQuota(IN POOL_TYPE PoolType
,
698 IN SIZE_T NumberOfBytes
)
703 return ExAllocatePoolWithQuotaTag(PoolType
, NumberOfBytes
, 'enoN');
711 ExAllocatePoolWithTagPriority(IN POOL_TYPE PoolType
,
712 IN SIZE_T NumberOfBytes
,
714 IN EX_POOL_PRIORITY Priority
)
720 return ExAllocatePoolWithTag(PoolType
, NumberOfBytes
, Tag
);
728 ExAllocatePoolWithQuotaTag(IN POOL_TYPE PoolType
,
729 IN SIZE_T NumberOfBytes
,
736 return ExAllocatePoolWithTag(PoolType
, NumberOfBytes
, Tag
);