1 /* 2 * PROJECT: ReactOS win32 kernel mode subsystem 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: win32ss/gdi/ntgdi/gdipool.c 5 * PURPOSE: Static size allocator for user mode object attributes 6 * PROGRAMMERS: Timo Kreuzer 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include <win32k.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 typedef struct _GDI_POOL_SECTION 16 { 17 LIST_ENTRY leInUseLink; 18 LIST_ENTRY leReadyLink; 19 20 PVOID pvBaseAddress; 21 22 ULONG ulCommitBitmap; 23 ULONG cAllocCount; 24 25 RTL_BITMAP bitmap; 26 ULONG aulBits[1]; 27 } GDI_POOL_SECTION, *PGDI_POOL_SECTION; 28 29 typedef struct _GDI_POOL 30 { 31 ULONG ulTag; 32 ULONG cjAllocSize; 33 ULONG cjSectionSize; // 32 * cjAllocSize, rounded up to pages 34 ULONG cSlotsPerSection; 35 ULONG cEmptySections; 36 EX_PUSH_LOCK pushlock; // For pool growth 37 #if DBG_ENABLE_EVENT_LOGGING 38 SLIST_HEADER slhLog; 39 #endif 40 41 LIST_ENTRY leInUseList; 42 LIST_ENTRY leEmptyList; 43 LIST_ENTRY leReadyList; 44 } GDI_POOL; 45 46 #define GDI_POOL_ALLOCATION_GRANULARITY 64 * 1024 47 48 static 49 PGDI_POOL_SECTION 50 GdiPoolAllocateSection(PGDI_POOL pPool) 51 { 52 PGDI_POOL_SECTION pSection; 53 PVOID pvBaseAddress; 54 SIZE_T cjSize; 55 NTSTATUS status; 56 57 /* Allocate a section object */ 58 cjSize = sizeof(GDI_POOL_SECTION) + pPool->cSlotsPerSection / sizeof(ULONG); 59 pSection = EngAllocMem(0, cjSize, pPool->ulTag); 60 if (!pSection) 61 { 62 return NULL; 63 } 64 65 /* Reserve user mode memory */ 66 cjSize = GDI_POOL_ALLOCATION_GRANULARITY; 67 pvBaseAddress = NULL; 68 status = ZwAllocateVirtualMemory(NtCurrentProcess(), 69 &pvBaseAddress, 70 0, 71 &cjSize, 72 MEM_RESERVE, 73 PAGE_READWRITE); 74 if (!NT_SUCCESS(status)) 75 { 76 EngFreeMem(pSection); 77 return NULL; 78 } 79 80 /* Initialize the section */ 81 pSection->pvBaseAddress = pvBaseAddress; 82 pSection->ulCommitBitmap = 0; 83 pSection->cAllocCount = 0; 84 RtlInitializeBitMap(&pSection->bitmap, 85 pSection->aulBits, 86 pPool->cSlotsPerSection); 87 RtlClearAllBits(&pSection->bitmap); 88 89 /* Return the section */ 90 return pSection; 91 } 92 93 static 94 VOID 95 GdiPoolDeleteSection(PGDI_POOL pPool, PGDI_POOL_SECTION pSection) 96 { 97 NTSTATUS status; 98 SIZE_T cjSize = 0; 99 100 /* Should not have any allocations */ 101 if (pSection->cAllocCount != 0) 102 { 103 DPRINT1("There are %lu allocations left, section=%p, pool=%p\n", 104 pSection->cAllocCount, pSection, pPool); 105 DBG_DUMP_EVENT_LIST(&pPool->slhLog); 106 ASSERT(FALSE); 107 } 108 109 /* Release the virtual memory */ 110 status = ZwFreeVirtualMemory(NtCurrentProcess(), 111 &pSection->pvBaseAddress, 112 &cjSize, 113 MEM_RELEASE); 114 ASSERT(NT_SUCCESS(status)); 115 116 /* Free the section object */ 117 EngFreeMem(pSection); 118 } 119 120 PVOID 121 NTAPI 122 GdiPoolAllocate( 123 PGDI_POOL pPool) 124 { 125 PGDI_POOL_SECTION pSection; 126 ULONG ulIndex, cjOffset, ulPageBit; 127 PLIST_ENTRY ple; 128 PVOID pvAlloc, pvBaseAddress; 129 SIZE_T cjSize; 130 NTSTATUS status; 131 132 /* Disable APCs and acquire the pool lock */ 133 KeEnterCriticalRegion(); 134 ExAcquirePushLockExclusive(&pPool->pushlock); 135 136 /* Check if we have a ready section */ 137 if (!IsListEmpty(&pPool->leReadyList)) 138 { 139 /* Get a free section */ 140 ple = pPool->leReadyList.Flink; 141 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leReadyLink); 142 if (pSection->cAllocCount >= pPool->cSlotsPerSection) 143 { 144 DPRINT1("pSection->cAllocCount=%lu, pPool->cSlotsPerSection=%lu\n", 145 pSection->cAllocCount, pPool->cSlotsPerSection); 146 DBG_DUMP_EVENT_LIST(&pPool->slhLog); 147 ASSERT(FALSE); 148 } 149 ASSERT(pSection->cAllocCount < pPool->cSlotsPerSection); 150 } 151 else 152 { 153 /* No, check if we have something on the empty list */ 154 if (!IsListEmpty(&pPool->leEmptyList)) 155 { 156 /* Yes, remove it from the empty list */ 157 ple = RemoveHeadList(&pPool->leEmptyList); 158 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink); 159 pPool->cEmptySections--; 160 ASSERT(pSection->cAllocCount == 0); 161 } 162 else 163 { 164 /* No, allocate a new section */ 165 pSection = GdiPoolAllocateSection(pPool); 166 if (!pSection) 167 { 168 DPRINT1("Couldn't allocate a section\n"); 169 pvAlloc = NULL; 170 goto done; 171 } 172 } 173 174 /* Insert it into the in-use and ready list */ 175 InsertHeadList(&pPool->leInUseList, &pSection->leInUseLink); 176 InsertHeadList(&pPool->leReadyList, &pSection->leReadyLink); 177 } 178 179 /* Find and set a single bit */ 180 ulIndex = RtlFindClearBitsAndSet(&pSection->bitmap, 1, 0); 181 ASSERT(ulIndex != MAXULONG); 182 183 /* Calculate the allocation address */ 184 cjOffset = ulIndex * pPool->cjAllocSize; 185 pvAlloc = (PVOID)((ULONG_PTR)pSection->pvBaseAddress + cjOffset); 186 187 /* Check if memory is comitted */ 188 ulPageBit = 1 << (cjOffset / PAGE_SIZE); 189 ulPageBit |= 1 << ((cjOffset + pPool->cjAllocSize - 1) / PAGE_SIZE); 190 if ((pSection->ulCommitBitmap & ulPageBit) != ulPageBit) 191 { 192 /* Commit the pages */ 193 pvBaseAddress = PAGE_ALIGN(pvAlloc); 194 cjSize = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pvAlloc, pPool->cjAllocSize) * PAGE_SIZE; 195 status = ZwAllocateVirtualMemory(NtCurrentProcess(), 196 &pvBaseAddress, 197 0, 198 &cjSize, 199 MEM_COMMIT, 200 PAGE_READWRITE); 201 if (!NT_SUCCESS(status)) 202 { 203 pvAlloc = NULL; 204 goto done; 205 } 206 207 pSection->ulCommitBitmap |= ulPageBit; 208 } 209 210 /* Increase alloc count */ 211 pSection->cAllocCount++; 212 ASSERT(RtlNumberOfSetBits(&pSection->bitmap) == pSection->cAllocCount); 213 DBG_LOGEVENT(&pPool->slhLog, EVENT_ALLOCATE, pvAlloc); 214 215 /* Check if section is now busy */ 216 if (pSection->cAllocCount == pPool->cSlotsPerSection) 217 { 218 /* Remove the section from the ready list */ 219 RemoveEntryList(&pSection->leReadyLink); 220 } 221 222 done: 223 /* Release the pool lock and enable APCs */ 224 ExReleasePushLockExclusive(&pPool->pushlock); 225 KeLeaveCriticalRegion(); 226 227 DPRINT("GdiPoolallocate: %p\n", pvAlloc); 228 return pvAlloc; 229 } 230 231 VOID 232 NTAPI 233 GdiPoolFree( 234 PGDI_POOL pPool, 235 PVOID pvAlloc) 236 { 237 PLIST_ENTRY ple; 238 PGDI_POOL_SECTION pSection = NULL; 239 ULONG_PTR cjOffset; 240 ULONG ulIndex; 241 DPRINT("GdiPoolFree: %p\n", pvAlloc); 242 243 /* Disable APCs and acquire the pool lock */ 244 KeEnterCriticalRegion(); 245 ExAcquirePushLockExclusive(&pPool->pushlock); 246 247 /* Loop all used sections */ 248 for (ple = pPool->leInUseList.Flink; 249 ple != &pPool->leInUseList; 250 ple = ple->Flink) 251 { 252 /* Get the pointer to the section */ 253 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink); 254 255 /* Calculate offset */ 256 cjOffset = (ULONG_PTR)pvAlloc - (ULONG_PTR)pSection->pvBaseAddress; 257 258 /* Check if the allocation is from this section */ 259 if (cjOffset < pPool->cjSectionSize) 260 { 261 /* Calculate the index of the allocation */ 262 ulIndex = cjOffset / pPool->cjAllocSize; 263 264 /* Mark it as free */ 265 ASSERT(RtlTestBit(&pSection->bitmap, ulIndex) == TRUE); 266 RtlClearBit(&pSection->bitmap, ulIndex); 267 268 /* Decrease allocation count */ 269 pSection->cAllocCount--; 270 ASSERT(RtlNumberOfSetBits(&pSection->bitmap) == pSection->cAllocCount); 271 DBG_LOGEVENT(&pPool->slhLog, EVENT_FREE, pvAlloc); 272 273 /* Check if the section got valid now */ 274 if (pSection->cAllocCount == pPool->cSlotsPerSection - 1) 275 { 276 /* Insert it into the ready list */ 277 InsertTailList(&pPool->leReadyList, &pSection->leReadyLink); 278 } 279 /* Check if it got empty now */ 280 else if (pSection->cAllocCount == 0) 281 { 282 /* Remove the section from the lists */ 283 RemoveEntryList(&pSection->leInUseLink); 284 RemoveEntryList(&pSection->leReadyLink); 285 286 if (pPool->cEmptySections >= 1) 287 { 288 /* Delete the section */ 289 GdiPoolDeleteSection(pPool, pSection); 290 } 291 else 292 { 293 /* Insert it into the empty list */ 294 InsertHeadList(&pPool->leEmptyList, &pSection->leInUseLink); 295 pPool->cEmptySections++; 296 } 297 } 298 299 goto done; 300 } 301 } 302 303 DbgPrint("failed to free. pvAlloc=%p, base=%p, size=%lx\n", 304 pvAlloc, pSection ? pSection->pvBaseAddress : NULL, pPool->cjSectionSize); 305 ASSERT(FALSE); 306 // KeBugCheck() 307 308 done: 309 /* Release the pool lock and enable APCs */ 310 ExReleasePushLockExclusive(&pPool->pushlock); 311 KeLeaveCriticalRegion(); 312 } 313 314 PGDI_POOL 315 NTAPI 316 GdiPoolCreate( 317 ULONG cjAllocSize, 318 ULONG ulTag) 319 { 320 PGDI_POOL pPool; 321 322 /* Allocate a pool object */ 323 pPool = EngAllocMem(0, sizeof(GDI_POOL), 'lopG'); 324 if (!pPool) return NULL; 325 326 /* Initialize the object */ 327 ExInitializePushLock(&pPool->pushlock); 328 InitializeListHead(&pPool->leInUseList); 329 InitializeListHead(&pPool->leReadyList); 330 InitializeListHead(&pPool->leEmptyList); 331 pPool->cEmptySections = 0; 332 pPool->cjAllocSize = cjAllocSize; 333 pPool->ulTag = ulTag; 334 pPool->cjSectionSize = GDI_POOL_ALLOCATION_GRANULARITY; 335 pPool->cSlotsPerSection = pPool->cjSectionSize / cjAllocSize; 336 DBG_INITLOG(&pPool->slhLog); 337 338 return pPool; 339 } 340 341 VOID 342 NTAPI 343 GdiPoolDestroy(PGDI_POOL pPool) 344 { 345 PGDI_POOL_SECTION pSection; 346 PLIST_ENTRY ple; 347 348 /* Loop all empty sections, removing them */ 349 while (!IsListEmpty(&pPool->leEmptyList)) 350 { 351 /* Delete the section */ 352 ple = RemoveHeadList(&pPool->leEmptyList); 353 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink); 354 GdiPoolDeleteSection(pPool, pSection); 355 } 356 357 /* Loop all ready sections, removing them */ 358 while (!IsListEmpty(&pPool->leInUseList)) 359 { 360 /* Delete the section */ 361 ple = RemoveHeadList(&pPool->leInUseList); 362 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink); 363 GdiPoolDeleteSection(pPool, pSection); 364 } 365 366 DBG_CLEANUP_EVENT_LIST(&pPool->slhLog); 367 368 EngFreeMem(pPool); 369 } 370