mirror of
https://github.com/reactos/reactos.git
synced 2025-01-03 21:09:19 +00:00
[WIN32K]
Implement gdi pool. An allocator for user mode gdi object attributes. The old method allocated a 4k page for every object, wasting 4k physical memory and 64k address space (allcoation granularity) The new allocator creates a per process pool for each object attribute type. Allocations are done from "sections" that start with 1 page and grow dynamically up to 64k, if neccessary a new section is allocated. This will use about 1/10 of memory for dc attributes and 1/512 for brush attributes. Also allocation is way faster. Caching object attributes is not neccessary anymore. svn path=/trunk/; revision=51357
This commit is contained in:
parent
99c3b4ec57
commit
46a8c8f9fa
1 changed files with 336 additions and 0 deletions
336
reactos/subsystems/win32/win32k/objects/gdipool.c
Normal file
336
reactos/subsystems/win32/win32k/objects/gdipool.c
Normal file
|
@ -0,0 +1,336 @@
|
||||||
|
/*
|
||||||
|
* PROJECT: ReactOS win32 kernel mode subsystem
|
||||||
|
* LICENSE: GPL - See COPYING in the top level directory
|
||||||
|
* FILE: subsystems/win32/win32k/objects/gdiobj.c
|
||||||
|
* PURPOSE: Static size allocator for user mode object attributes
|
||||||
|
* PROGRAMMERS: Timo Kreuzer
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* INCLUDES ******************************************************************/
|
||||||
|
|
||||||
|
#include <win32k.h>
|
||||||
|
#define NDEBUG
|
||||||
|
#include <debug.h>
|
||||||
|
|
||||||
|
typedef struct _GDI_POOL_SECTION
|
||||||
|
{
|
||||||
|
LIST_ENTRY leInUseLink;
|
||||||
|
LIST_ENTRY leReadyLink;
|
||||||
|
|
||||||
|
PVOID pvBaseAddress;
|
||||||
|
|
||||||
|
ULONG ulCommitBitmap;
|
||||||
|
ULONG cAllocCount;
|
||||||
|
|
||||||
|
RTL_BITMAP bitmap;
|
||||||
|
ULONG aulBits[1];
|
||||||
|
} GDI_POOL_SECTION, *PGDI_POOL_SECTION;
|
||||||
|
|
||||||
|
typedef struct _GDI_POOL
|
||||||
|
{
|
||||||
|
ULONG ulTag;
|
||||||
|
ULONG cjAllocSize;
|
||||||
|
ULONG cjSectionSize; // 32 * cjAllocSize, rounded up to pages
|
||||||
|
ULONG cSlotsPerSection;
|
||||||
|
ULONG cEmptySections;
|
||||||
|
EX_PUSH_LOCK pushlock; // for pool growth
|
||||||
|
|
||||||
|
LIST_ENTRY leInUseList;
|
||||||
|
LIST_ENTRY leEmptyList;
|
||||||
|
LIST_ENTRY leReadyList;
|
||||||
|
} GDI_POOL;
|
||||||
|
|
||||||
|
#define GDI_POOL_ALLOCATION_GRANULARITY 64 * 1024
|
||||||
|
|
||||||
|
static
|
||||||
|
PGDI_POOL_SECTION
|
||||||
|
GdiPoolAllocateSection(PGDI_POOL pPool)
|
||||||
|
{
|
||||||
|
PGDI_POOL_SECTION pSection;
|
||||||
|
PVOID pvBaseAddress;
|
||||||
|
SIZE_T cjSize;
|
||||||
|
NTSTATUS status;
|
||||||
|
|
||||||
|
/* Allocate a section object */
|
||||||
|
cjSize = sizeof(GDI_POOL_SECTION) + pPool->cSlotsPerSection / sizeof(ULONG);
|
||||||
|
pSection = EngAllocMem(0, cjSize, pPool->ulTag);
|
||||||
|
if (!pSection)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reserve user mode memory */
|
||||||
|
cjSize = GDI_POOL_ALLOCATION_GRANULARITY;
|
||||||
|
pvBaseAddress = NULL;
|
||||||
|
status = ZwAllocateVirtualMemory(NtCurrentProcess(),
|
||||||
|
&pvBaseAddress,
|
||||||
|
0,
|
||||||
|
&cjSize,
|
||||||
|
MEM_RESERVE,
|
||||||
|
PAGE_READWRITE);
|
||||||
|
if (!NT_SUCCESS(status))
|
||||||
|
{
|
||||||
|
EngFreeMem(pSection);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the section */
|
||||||
|
pSection->pvBaseAddress = pvBaseAddress;
|
||||||
|
pSection->ulCommitBitmap = 0;
|
||||||
|
pSection->cAllocCount = 0;
|
||||||
|
RtlInitializeBitMap(&pSection->bitmap,
|
||||||
|
pSection->aulBits,
|
||||||
|
pPool->cSlotsPerSection);
|
||||||
|
RtlClearAllBits(&pSection->bitmap);
|
||||||
|
|
||||||
|
/* Return the section */
|
||||||
|
return pSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
VOID
|
||||||
|
GdiPoolDeleteSection(PGDI_POOL pPool, PGDI_POOL_SECTION pSection)
|
||||||
|
{
|
||||||
|
NTSTATUS status;
|
||||||
|
SIZE_T cjSize = 0;
|
||||||
|
|
||||||
|
/* Should not have any allocations */
|
||||||
|
ASSERT(pSection->cAllocCount == 0);
|
||||||
|
|
||||||
|
/* Release the virtual memory */
|
||||||
|
status = ZwFreeVirtualMemory(NtCurrentProcess(),
|
||||||
|
&pSection->pvBaseAddress,
|
||||||
|
&cjSize,
|
||||||
|
MEM_RELEASE);
|
||||||
|
ASSERT(NT_SUCCESS(status));
|
||||||
|
|
||||||
|
/* Free the section object */
|
||||||
|
EngFreeMem(pSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
PVOID
|
||||||
|
NTAPI
|
||||||
|
GdiPoolAllocate(
|
||||||
|
PGDI_POOL pPool)
|
||||||
|
{
|
||||||
|
PGDI_POOL_SECTION pSection;
|
||||||
|
ULONG ulIndex, cjOffset, ulPageBit;
|
||||||
|
PLIST_ENTRY ple;
|
||||||
|
PVOID pvAlloc, pvBaseAddress;
|
||||||
|
SIZE_T cjSize;
|
||||||
|
NTSTATUS status;
|
||||||
|
|
||||||
|
/* Disable APCs and acquire the pool lock */
|
||||||
|
KeEnterCriticalRegion();
|
||||||
|
ExAcquirePushLockExclusive(&pPool->pushlock);
|
||||||
|
|
||||||
|
/* Check if we have a ready section */
|
||||||
|
if (!IsListEmpty(&pPool->leReadyList))
|
||||||
|
{
|
||||||
|
/* Get a free section */
|
||||||
|
ple = pPool->leReadyList.Flink;
|
||||||
|
pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leReadyLink);
|
||||||
|
ASSERT(pSection->cAllocCount < pPool->cSlotsPerSection);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* No, check if we have something on the empty list */
|
||||||
|
if (!IsListEmpty(&pPool->leEmptyList))
|
||||||
|
{
|
||||||
|
/* Yes, remove it from the empty list */
|
||||||
|
ple = RemoveHeadList(&pPool->leEmptyList);
|
||||||
|
pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* No, allocate a new section */
|
||||||
|
pSection = GdiPoolAllocateSection(pPool);
|
||||||
|
if (!pSection)
|
||||||
|
{
|
||||||
|
DPRINT1("Couldn't allocate a section\n");
|
||||||
|
pvAlloc = NULL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert it into the ready list */
|
||||||
|
InsertHeadList(&pPool->leReadyList, &pSection->leReadyLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert it into the in-use list */
|
||||||
|
InsertHeadList(&pPool->leInUseList, &pSection->leInUseLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find and set a single bit */
|
||||||
|
ulIndex = RtlFindClearBitsAndSet(&pSection->bitmap, 1, 0);
|
||||||
|
ASSERT(ulIndex != MAXULONG);
|
||||||
|
|
||||||
|
/* Calculate the allocation address */
|
||||||
|
cjOffset = ulIndex * pPool->cjAllocSize;
|
||||||
|
pvAlloc = (PVOID)((ULONG_PTR)pSection->pvBaseAddress + cjOffset);
|
||||||
|
|
||||||
|
/* Check if memory is comitted */
|
||||||
|
ulPageBit = 1 << (cjOffset / PAGE_SIZE);
|
||||||
|
ulPageBit |= 1 << ((cjOffset + pPool->cjAllocSize - 1) / PAGE_SIZE);
|
||||||
|
if ((pSection->ulCommitBitmap & ulPageBit) != ulPageBit)
|
||||||
|
{
|
||||||
|
/* Commit the pages */
|
||||||
|
pvBaseAddress = PAGE_ALIGN(pvAlloc);
|
||||||
|
cjSize = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pvAlloc, pPool->cjAllocSize) * PAGE_SIZE;
|
||||||
|
status = ZwAllocateVirtualMemory(NtCurrentProcess(),
|
||||||
|
&pvBaseAddress,
|
||||||
|
0,
|
||||||
|
&cjSize,
|
||||||
|
MEM_COMMIT,
|
||||||
|
PAGE_READWRITE);
|
||||||
|
|
||||||
|
pSection->ulCommitBitmap |= ulPageBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Increase alloc count and check if section is now busy */
|
||||||
|
pSection->cAllocCount++;
|
||||||
|
if (pSection->cAllocCount == pPool->cSlotsPerSection)
|
||||||
|
{
|
||||||
|
/* Remove the section from the ready list */
|
||||||
|
RemoveEntryList(&pSection->leReadyLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
/* Release the pool lock and enable APCs */
|
||||||
|
ExReleasePushLockExclusive(&pPool->pushlock);
|
||||||
|
KeLeaveCriticalRegion();
|
||||||
|
DPRINT1("GdiPoolallocate: %p\n", pvAlloc);
|
||||||
|
|
||||||
|
return pvAlloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VOID
|
||||||
|
NTAPI
|
||||||
|
GdiPoolFree(
|
||||||
|
PGDI_POOL pPool,
|
||||||
|
PVOID pvAlloc)
|
||||||
|
{
|
||||||
|
PLIST_ENTRY ple;
|
||||||
|
PGDI_POOL_SECTION pSection;
|
||||||
|
ULONG_PTR cjOffset;
|
||||||
|
ULONG ulIndex;
|
||||||
|
DPRINT1("GdiPoolFree: %p\n", pvAlloc);
|
||||||
|
|
||||||
|
/* Disable APCs and acquire the pool lock */
|
||||||
|
KeEnterCriticalRegion();
|
||||||
|
ExAcquirePushLockExclusive(&pPool->pushlock);
|
||||||
|
|
||||||
|
/* Loop all used sections */
|
||||||
|
for (ple = pPool->leInUseList.Flink;
|
||||||
|
ple != &pPool->leInUseList;
|
||||||
|
ple = ple->Flink)
|
||||||
|
{
|
||||||
|
/* Get the pointer to the section */
|
||||||
|
pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
|
||||||
|
|
||||||
|
/* Calculate offset */
|
||||||
|
cjOffset = (ULONG_PTR)pvAlloc - (ULONG_PTR)pSection->pvBaseAddress;
|
||||||
|
|
||||||
|
/* Check if the allocation is from this section */
|
||||||
|
if (cjOffset < pPool->cjSectionSize)
|
||||||
|
{
|
||||||
|
/* Calculate the index of the allocation */
|
||||||
|
ulIndex = cjOffset / pPool->cjAllocSize;
|
||||||
|
|
||||||
|
/* Mark it as free */
|
||||||
|
ASSERT(RtlTestBit(&pSection->bitmap, ulIndex) == TRUE);
|
||||||
|
RtlClearBit(&pSection->bitmap, ulIndex);
|
||||||
|
|
||||||
|
/* Decrease allocation count */
|
||||||
|
pSection->cAllocCount--;
|
||||||
|
|
||||||
|
/* Check if the section got valid now */
|
||||||
|
if (pSection->cAllocCount == pPool->cSlotsPerSection - 1)
|
||||||
|
{
|
||||||
|
/* Insert it into the ready list */
|
||||||
|
InsertTailList(&pPool->leReadyList, &pSection->leReadyLink);
|
||||||
|
}
|
||||||
|
/* Check if it got empty now */
|
||||||
|
else if (pSection->cAllocCount == 0)
|
||||||
|
{
|
||||||
|
/* Remove the section from the lists */
|
||||||
|
RemoveEntryList(&pSection->leInUseLink);
|
||||||
|
RemoveEntryList(&pSection->leReadyLink);
|
||||||
|
|
||||||
|
if (pPool->cEmptySections > 1)
|
||||||
|
{
|
||||||
|
/* Delete the section */
|
||||||
|
GdiPoolDeleteSection(pPool, pSection);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Insert it into the empty list */
|
||||||
|
InsertHeadList(&pPool->leEmptyList, &pSection->leInUseLink);
|
||||||
|
pPool->cEmptySections++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(FALSE);
|
||||||
|
// KeBugCheck()
|
||||||
|
|
||||||
|
done:
|
||||||
|
/* Release the pool lock and enable APCs */
|
||||||
|
ExReleasePushLockExclusive(&pPool->pushlock);
|
||||||
|
KeLeaveCriticalRegion();
|
||||||
|
}
|
||||||
|
|
||||||
|
PGDI_POOL
|
||||||
|
NTAPI
|
||||||
|
GdiPoolCreate(
|
||||||
|
ULONG cjAllocSize,
|
||||||
|
ULONG ulTag)
|
||||||
|
{
|
||||||
|
PGDI_POOL pPool;
|
||||||
|
|
||||||
|
/* Allocate a pool object */
|
||||||
|
pPool = EngAllocMem(0, sizeof(GDI_POOL), 'lopG');
|
||||||
|
if (!pPool) return NULL;
|
||||||
|
|
||||||
|
/* Initialize the object */
|
||||||
|
ExInitializePushLock(&pPool->pushlock);
|
||||||
|
InitializeListHead(&pPool->leInUseList);
|
||||||
|
InitializeListHead(&pPool->leReadyList);
|
||||||
|
InitializeListHead(&pPool->leEmptyList);
|
||||||
|
pPool->cEmptySections = 0;
|
||||||
|
pPool->cjAllocSize = cjAllocSize;
|
||||||
|
pPool->ulTag = ulTag;
|
||||||
|
pPool->cjSectionSize = GDI_POOL_ALLOCATION_GRANULARITY;
|
||||||
|
pPool->cSlotsPerSection = pPool->cjSectionSize / cjAllocSize;
|
||||||
|
|
||||||
|
return pPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID
|
||||||
|
NTAPI
|
||||||
|
GdiPoolDestroy(PGDI_POOL pPool)
|
||||||
|
{
|
||||||
|
PGDI_POOL_SECTION pSection;
|
||||||
|
PLIST_ENTRY ple;
|
||||||
|
|
||||||
|
/* Loop all empty sections, removing them */
|
||||||
|
while ((ple = RemoveHeadList(&pPool->leEmptyList)))
|
||||||
|
{
|
||||||
|
/* Delete the section */
|
||||||
|
pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
|
||||||
|
GdiPoolDeleteSection(pPool, pSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop all ready sections, removing them */
|
||||||
|
while ((ple = RemoveHeadList(&pPool->leInUseList)))
|
||||||
|
{
|
||||||
|
/* Delete the section */
|
||||||
|
pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
|
||||||
|
GdiPoolDeleteSection(pPool, pSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
EngFreeMem(pPool);
|
||||||
|
}
|
Loading…
Reference in a new issue