[CMAKE]
[reactos.git] / subsystems / win32 / win32k / objects / gdipool.c
1 /*
2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: subsystems/win32/win32k/objects/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 %ld 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 ASSERT(pSection->cAllocCount < pPool->cSlotsPerSection);
143 }
144 else
145 {
146 /* No, check if we have something on the empty list */
147 if (!IsListEmpty(&pPool->leEmptyList))
148 {
149 /* Yes, remove it from the empty list */
150 ple = RemoveHeadList(&pPool->leEmptyList);
151 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
152 }
153 else
154 {
155 /* No, allocate a new section */
156 pSection = GdiPoolAllocateSection(pPool);
157 if (!pSection)
158 {
159 DPRINT1("Couldn't allocate a section\n");
160 pvAlloc = NULL;
161 goto done;
162 }
163
164 /* Insert it into the ready list */
165 InsertHeadList(&pPool->leReadyList, &pSection->leReadyLink);
166 }
167
168 /* Insert it into the in-use list */
169 InsertHeadList(&pPool->leInUseList, &pSection->leInUseLink);
170 }
171
172 /* Find and set a single bit */
173 ulIndex = RtlFindClearBitsAndSet(&pSection->bitmap, 1, 0);
174 ASSERT(ulIndex != MAXULONG);
175
176 /* Calculate the allocation address */
177 cjOffset = ulIndex * pPool->cjAllocSize;
178 pvAlloc = (PVOID)((ULONG_PTR)pSection->pvBaseAddress + cjOffset);
179
180 /* Check if memory is comitted */
181 ulPageBit = 1 << (cjOffset / PAGE_SIZE);
182 ulPageBit |= 1 << ((cjOffset + pPool->cjAllocSize - 1) / PAGE_SIZE);
183 if ((pSection->ulCommitBitmap & ulPageBit) != ulPageBit)
184 {
185 /* Commit the pages */
186 pvBaseAddress = PAGE_ALIGN(pvAlloc);
187 cjSize = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pvAlloc, pPool->cjAllocSize) * PAGE_SIZE;
188 status = ZwAllocateVirtualMemory(NtCurrentProcess(),
189 &pvBaseAddress,
190 0,
191 &cjSize,
192 MEM_COMMIT,
193 PAGE_READWRITE);
194
195 pSection->ulCommitBitmap |= ulPageBit;
196 }
197
198 /* Increase alloc count */
199 pSection->cAllocCount++;
200 DBG_LOGEVENT(&pPool->slhLog, EVENT_ALLOCATE, pvAlloc);
201
202 /* Check if section is now busy */
203 if (pSection->cAllocCount == pPool->cSlotsPerSection)
204 {
205 /* Remove the section from the ready list */
206 RemoveEntryList(&pSection->leReadyLink);
207 }
208
209 done:
210 /* Release the pool lock and enable APCs */
211 ExReleasePushLockExclusive(&pPool->pushlock);
212 KeLeaveCriticalRegion();
213
214 DPRINT("GdiPoolallocate: %p\n", pvAlloc);
215 return pvAlloc;
216 }
217
218 VOID
219 NTAPI
220 GdiPoolFree(
221 PGDI_POOL pPool,
222 PVOID pvAlloc)
223 {
224 PLIST_ENTRY ple;
225 PGDI_POOL_SECTION pSection = NULL;
226 ULONG_PTR cjOffset;
227 ULONG ulIndex;
228 DPRINT("GdiPoolFree: %p\n", pvAlloc);
229
230 /* Disable APCs and acquire the pool lock */
231 KeEnterCriticalRegion();
232 ExAcquirePushLockExclusive(&pPool->pushlock);
233
234 /* Loop all used sections */
235 for (ple = pPool->leInUseList.Flink;
236 ple != &pPool->leInUseList;
237 ple = ple->Flink)
238 {
239 /* Get the pointer to the section */
240 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
241
242 /* Calculate offset */
243 cjOffset = (ULONG_PTR)pvAlloc - (ULONG_PTR)pSection->pvBaseAddress;
244
245 /* Check if the allocation is from this section */
246 if (cjOffset < pPool->cjSectionSize)
247 {
248 /* Calculate the index of the allocation */
249 ulIndex = cjOffset / pPool->cjAllocSize;
250
251 /* Mark it as free */
252 ASSERT(RtlTestBit(&pSection->bitmap, ulIndex) == TRUE);
253 RtlClearBit(&pSection->bitmap, ulIndex);
254
255 /* Decrease allocation count */
256 pSection->cAllocCount--;
257 DBG_LOGEVENT(&pPool->slhLog, EVENT_FREE, pvAlloc);
258
259 /* Check if the section got valid now */
260 if (pSection->cAllocCount == pPool->cSlotsPerSection - 1)
261 {
262 /* Insert it into the ready list */
263 InsertTailList(&pPool->leReadyList, &pSection->leReadyLink);
264 }
265 /* Check if it got empty now */
266 else if (pSection->cAllocCount == 0)
267 {
268 /* Remove the section from the lists */
269 RemoveEntryList(&pSection->leInUseLink);
270 RemoveEntryList(&pSection->leReadyLink);
271
272 if (pPool->cEmptySections > 1)
273 {
274 /* Delete the section */
275 GdiPoolDeleteSection(pPool, pSection);
276 }
277 else
278 {
279 /* Insert it into the empty list */
280 InsertHeadList(&pPool->leEmptyList, &pSection->leInUseLink);
281 pPool->cEmptySections++;
282 }
283 }
284
285 goto done;
286 }
287 }
288
289 DbgPrint("failed to free. pvAlloc=%p, base=%p, size=%lx\n",
290 pvAlloc, pSection->pvBaseAddress, pPool->cjSectionSize);
291 ASSERT(FALSE);
292 // KeBugCheck()
293
294 done:
295 /* Release the pool lock and enable APCs */
296 ExReleasePushLockExclusive(&pPool->pushlock);
297 KeLeaveCriticalRegion();
298 }
299
300 PGDI_POOL
301 NTAPI
302 GdiPoolCreate(
303 ULONG cjAllocSize,
304 ULONG ulTag)
305 {
306 PGDI_POOL pPool;
307
308 /* Allocate a pool object */
309 pPool = EngAllocMem(0, sizeof(GDI_POOL), 'lopG');
310 if (!pPool) return NULL;
311
312 /* Initialize the object */
313 ExInitializePushLock(&pPool->pushlock);
314 InitializeListHead(&pPool->leInUseList);
315 InitializeListHead(&pPool->leReadyList);
316 InitializeListHead(&pPool->leEmptyList);
317 pPool->cEmptySections = 0;
318 pPool->cjAllocSize = cjAllocSize;
319 pPool->ulTag = ulTag;
320 pPool->cjSectionSize = GDI_POOL_ALLOCATION_GRANULARITY;
321 pPool->cSlotsPerSection = pPool->cjSectionSize / cjAllocSize;
322 DBG_INITLOG(&pPool->slhLog);
323
324 return pPool;
325 }
326
327 VOID
328 NTAPI
329 GdiPoolDestroy(PGDI_POOL pPool)
330 {
331 PGDI_POOL_SECTION pSection;
332 PLIST_ENTRY ple;
333
334 /* Loop all empty sections, removing them */
335 while (!IsListEmpty(&pPool->leEmptyList))
336 {
337 /* Delete the section */
338 ple = RemoveHeadList(&pPool->leEmptyList);
339 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
340 GdiPoolDeleteSection(pPool, pSection);
341 }
342
343 /* Loop all ready sections, removing them */
344 while (!IsListEmpty(&pPool->leInUseList))
345 {
346 /* Delete the section */
347 ple = RemoveHeadList(&pPool->leInUseList);
348 pSection = CONTAINING_RECORD(ple, GDI_POOL_SECTION, leInUseLink);
349 GdiPoolDeleteSection(pPool, pSection);
350 }
351
352 DBG_CLEANUP_EVENT_LIST(&pPool->slhLog);
353
354 EngFreeMem(pPool);
355 }