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