Sync with trunk r64222.
[reactos.git] / ntoskrnl / config / cmalloc.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/config/cmalloc.c
5 * PURPOSE: Routines for allocating and freeing registry structures
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS *******************************************************************/
16
17 BOOLEAN CmpAllocInited;
18 KGUARDED_MUTEX CmpAllocBucketLock, CmpDelayAllocBucketLock;
19
20 LIST_ENTRY CmpFreeKCBListHead;
21 KGUARDED_MUTEX CmpDelayAllocBucketLock;
22 LIST_ENTRY CmpFreeDelayItemsListHead;
23
24 /* FUNCTIONS *****************************************************************/
25
26 VOID
27 NTAPI
28 INIT_FUNCTION
29 CmpInitCmPrivateAlloc(VOID)
30 {
31 /* Make sure we didn't already do this */
32 if (!CmpAllocInited)
33 {
34 /* Setup the lock and list */
35 KeInitializeGuardedMutex(&CmpAllocBucketLock);
36 InitializeListHead(&CmpFreeKCBListHead);
37 CmpAllocInited = TRUE;
38 }
39 }
40
41 VOID
42 NTAPI
43 INIT_FUNCTION
44 CmpInitCmPrivateDelayAlloc(VOID)
45 {
46 /* Initialize the delay allocation list and lock */
47 KeInitializeGuardedMutex(&CmpDelayAllocBucketLock);
48 InitializeListHead(&CmpFreeDelayItemsListHead);
49 }
50
51 VOID
52 NTAPI
53 CmpFreeKeyControlBlock(IN PCM_KEY_CONTROL_BLOCK Kcb)
54 {
55 ULONG i;
56 PCM_ALLOC_PAGE AllocPage;
57 PAGED_CODE();
58
59 /* Sanity checks */
60 ASSERT(IsListEmpty(&Kcb->KeyBodyListHead) == TRUE);
61 for (i = 0; i < 4; i++) ASSERT(Kcb->KeyBodyArray[i] == NULL);
62
63 /* Check if it wasn't privately allocated */
64 if (!Kcb->PrivateAlloc)
65 {
66 /* Free it from the pool */
67 CmpFree(Kcb, 0);
68 return;
69 }
70
71 /* Acquire the private allocation lock */
72 KeAcquireGuardedMutex(&CmpAllocBucketLock);
73
74 /* Sanity check on lock ownership */
75 CMP_ASSERT_HASH_ENTRY_LOCK(Kcb->ConvKey);
76
77 /* Add us to the free list */
78 InsertTailList(&CmpFreeKCBListHead, &Kcb->FreeListEntry);
79
80 /* Get the allocation page */
81 AllocPage = CmpGetAllocPageFromKcb(Kcb);
82
83 /* Sanity check */
84 ASSERT(AllocPage->FreeCount != CM_KCBS_PER_PAGE);
85
86 /* Increase free count */
87 if (++AllocPage->FreeCount == CM_KCBS_PER_PAGE)
88 {
89 /* Loop all the entries */
90 for (i = 0; i < CM_KCBS_PER_PAGE; i++)
91 {
92 /* Get the KCB */
93 Kcb = (PVOID)((ULONG_PTR)AllocPage +
94 FIELD_OFFSET(CM_ALLOC_PAGE, AllocPage) +
95 i * sizeof(CM_KEY_CONTROL_BLOCK));
96
97 /* Remove the entry */
98 RemoveEntryList(&Kcb->FreeListEntry);
99 }
100
101 /* Free the page */
102 CmpFree(AllocPage, 0);
103 }
104
105 /* Release the lock */
106 KeReleaseGuardedMutex(&CmpAllocBucketLock);
107 }
108
109 PCM_KEY_CONTROL_BLOCK
110 NTAPI
111 CmpAllocateKeyControlBlock(VOID)
112 {
113 PLIST_ENTRY NextEntry;
114 PCM_KEY_CONTROL_BLOCK CurrentKcb;
115 PCM_ALLOC_PAGE AllocPage;
116 ULONG i;
117 PAGED_CODE();
118
119 /* Check if private allocations are initialized */
120 if (CmpAllocInited)
121 {
122 /* They are, acquire the bucket lock */
123 KeAcquireGuardedMutex(&CmpAllocBucketLock);
124
125 /* See if there's something on the free KCB list */
126 SearchKcbList:
127 if (!IsListEmpty(&CmpFreeKCBListHead))
128 {
129 /* Remove the entry */
130 NextEntry = RemoveHeadList(&CmpFreeKCBListHead);
131
132 /* Get the KCB */
133 CurrentKcb = CONTAINING_RECORD(NextEntry,
134 CM_KEY_CONTROL_BLOCK,
135 FreeListEntry);
136
137 /* Get the allocation page */
138 AllocPage = CmpGetAllocPageFromKcb(CurrentKcb);
139
140 /* Decrease the free count */
141 ASSERT(AllocPage->FreeCount != 0);
142 AllocPage->FreeCount--;
143
144 /* Make sure this KCB is privately allocated */
145 ASSERT(CurrentKcb->PrivateAlloc == 1);
146
147 /* Release the allocation lock */
148 KeReleaseGuardedMutex(&CmpAllocBucketLock);
149
150 /* Return the KCB */
151 return CurrentKcb;
152 }
153
154 /* Allocate an allocation page */
155 AllocPage = CmpAllocate(PAGE_SIZE, TRUE, TAG_CM);
156 if (AllocPage)
157 {
158 /* Set default entries */
159 AllocPage->FreeCount = CM_KCBS_PER_PAGE;
160
161 /* Loop each entry */
162 for (i = 0; i < CM_KCBS_PER_PAGE; i++)
163 {
164 /* Get this entry */
165 CurrentKcb = (PVOID)((ULONG_PTR)AllocPage +
166 FIELD_OFFSET(CM_ALLOC_PAGE, AllocPage) +
167 i * sizeof(CM_KEY_CONTROL_BLOCK));
168
169 /* Set it up */
170 CurrentKcb->PrivateAlloc = TRUE;
171 CurrentKcb->DelayCloseEntry = NULL;
172 InsertTailList(&CmpFreeKCBListHead,
173 &CurrentKcb->FreeListEntry);
174 }
175
176 /* Now go back and search the list */
177 goto SearchKcbList;
178 }
179 }
180
181 /* Allocate a KCB only */
182 CurrentKcb = CmpAllocate(sizeof(CM_KEY_CONTROL_BLOCK), TRUE, TAG_CM);
183 if (CurrentKcb)
184 {
185 /* Set it up */
186 CurrentKcb->PrivateAlloc = 0;
187 CurrentKcb->DelayCloseEntry = NULL;
188 }
189
190 /* Return it */
191 return CurrentKcb;
192 }
193
194 PVOID
195 NTAPI
196 CmpAllocateDelayItem(VOID)
197 {
198 PCM_DELAY_ALLOC Entry;
199 PCM_ALLOC_PAGE AllocPage;
200 ULONG i;
201 PLIST_ENTRY NextEntry;
202 PAGED_CODE();
203
204 /* Lock the allocation buckets */
205 KeAcquireGuardedMutex(&CmpDelayAllocBucketLock);
206
207 /* Look for an item on the free list */
208 SearchList:
209 if (!IsListEmpty(&CmpFreeDelayItemsListHead))
210 {
211 /* Get the current entry in the list */
212 NextEntry = RemoveHeadList(&CmpFreeDelayItemsListHead);
213
214 /* Grab the item */
215 Entry = CONTAINING_RECORD(NextEntry, CM_DELAY_ALLOC, ListEntry);
216
217 /* Clear the list */
218 Entry->ListEntry.Flink = Entry->ListEntry.Blink = NULL;
219
220 /* Grab the alloc page */
221 AllocPage = CmpGetAllocPageFromDelayAlloc(Entry);
222
223 /* Decrease free entries */
224 ASSERT(AllocPage->FreeCount != 0);
225 AllocPage->FreeCount--;
226
227 /* Release the lock */
228 KeReleaseGuardedMutex(&CmpDelayAllocBucketLock);
229 return Entry;
230 }
231
232 /* Allocate an allocation page */
233 AllocPage = CmpAllocate(PAGE_SIZE, TRUE, TAG_CM);
234 if (AllocPage)
235 {
236 /* Set default entries */
237 AllocPage->FreeCount = CM_DELAYS_PER_PAGE;
238
239 /* Loop each entry */
240 for (i = 0; i < CM_DELAYS_PER_PAGE; i++)
241 {
242 /* Get this entry and link it */
243 Entry = (PVOID)((ULONG_PTR)AllocPage +
244 FIELD_OFFSET(CM_ALLOC_PAGE, AllocPage) +
245 i * sizeof(CM_DELAY_ALLOC));
246 InsertTailList(&CmpFreeDelayItemsListHead,
247 &Entry->ListEntry);
248
249 /* Clear the KCB pointer */
250 Entry->Kcb = NULL;
251 }
252 }
253 else
254 {
255 /* Release the lock */
256 KeReleaseGuardedMutex(&CmpDelayAllocBucketLock);
257 return NULL;
258 }
259
260 /* Do the search again */
261 goto SearchList;
262 }
263
264 VOID
265 NTAPI
266 CmpFreeDelayItem(PVOID Entry)
267 {
268 PCM_DELAY_ALLOC AllocEntry = (PCM_DELAY_ALLOC)Entry;
269 PCM_ALLOC_PAGE AllocPage;
270 ULONG i;
271 PAGED_CODE();
272
273 /* Lock the table */
274 KeAcquireGuardedMutex(&CmpDelayAllocBucketLock);
275
276 /* Add the entry at the end */
277 InsertTailList(&CmpFreeDelayItemsListHead, &AllocEntry->ListEntry);
278
279 /* Get the alloc page */
280 AllocPage = CmpGetAllocPageFromDelayAlloc(Entry);
281 ASSERT(AllocPage->FreeCount != CM_DELAYS_PER_PAGE);
282
283 /* Increase the number of free items */
284 if (++AllocPage->FreeCount == CM_DELAYS_PER_PAGE)
285 {
286 /* Page is totally free now, loop each entry */
287 for (i = 0; i < CM_DELAYS_PER_PAGE; i++)
288 {
289 /* Get the entry and unlink it */
290 AllocEntry = (PVOID)((ULONG_PTR)AllocPage +
291 FIELD_OFFSET(CM_ALLOC_PAGE, AllocPage) +
292 i * sizeof(CM_DELAY_ALLOC));
293 RemoveEntryList(&AllocEntry->ListEntry);
294 }
295
296 /* Now free the page */
297 CmpFree(AllocPage, 0);
298 }
299
300 /* Release the lock */
301 KeReleaseGuardedMutex(&CmpDelayAllocBucketLock);
302 }