Author: tkreuzer Date: Fri Apr 15 20:14:44 2011 New Revision: 51357
URL: http://svn.reactos.org/svn/reactos?rev=51357&view=rev Log: [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.
Added: trunk/reactos/subsystems/win32/win32k/objects/gdipool.c (with props)
Added: trunk/reactos/subsystems/win32/win32k/objects/gdipool.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/subsystems/win32/win32k/obj... ============================================================================== --- trunk/reactos/subsystems/win32/win32k/objects/gdipool.c (added) +++ trunk/reactos/subsystems/win32/win32k/objects/gdipool.c [iso-8859-1] Fri Apr 15 20:14:44 2011 @@ -1,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); +}
Propchange: trunk/reactos/subsystems/win32/win32k/objects/gdipool.c ------------------------------------------------------------------------------ svn:eol-style = native