[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / mm / freelist.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/freelist.c
5 * PURPOSE: Handle the list of free physical pages
6 *
7 * PROGRAMMERS: David Welch (welch@cwcom.net)
8 * Robert Bergkvist
9 */
10
11 /* INCLUDES ****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16
17 #if defined (ALLOC_PRAGMA)
18 #pragma alloc_text(INIT, MmInitializePageList)
19 #endif
20
21 #define MODULE_INVOLVED_IN_ARM3
22 #include "ARM3/miarm.h"
23
24 /* GLOBALS ****************************************************************/
25
26 //
27 //
28 // ReactOS to NT Physical Page Descriptor Entry Legacy Mapping Definitions
29 //
30 // REACTOS NT
31 //
32 #define RmapListHead AweReferenceCount
33 #define PHYSICAL_PAGE MMPFN
34 #define PPHYSICAL_PAGE PMMPFN
35
36 /* The first array contains ReactOS PFNs, the second contains ARM3 PFNs */
37 PPHYSICAL_PAGE MmPfnDatabase[2];
38
39 PFN_NUMBER MmAvailablePages;
40 PFN_NUMBER MmResidentAvailablePages;
41 PFN_NUMBER MmResidentAvailableAtInit;
42
43 SIZE_T MmTotalCommitLimit;
44 SIZE_T MmTotalCommittedPages;
45 SIZE_T MmSharedCommit;
46 SIZE_T MmDriverCommit;
47 SIZE_T MmProcessCommit;
48 SIZE_T MmPagedPoolCommit;
49 SIZE_T MmPeakCommitment;
50 SIZE_T MmtotalCommitLimitMaximum;
51
52 KEVENT ZeroPageThreadEvent;
53 static BOOLEAN ZeroPageThreadShouldTerminate = FALSE;
54 static RTL_BITMAP MiUserPfnBitMap;
55
56 /* FUNCTIONS *************************************************************/
57
58 VOID
59 NTAPI
60 MiInitializeUserPfnBitmap(VOID)
61 {
62 PVOID Bitmap;
63
64 /* Allocate enough buffer for the PFN bitmap and align it on 32-bits */
65 Bitmap = ExAllocatePoolWithTag(NonPagedPool,
66 (((MmHighestPhysicalPage + 1) + 31) / 32) * 4,
67 ' mM');
68 ASSERT(Bitmap);
69
70 /* Initialize it and clear all the bits to begin with */
71 RtlInitializeBitMap(&MiUserPfnBitMap,
72 Bitmap,
73 MmHighestPhysicalPage + 1);
74 RtlClearAllBits(&MiUserPfnBitMap);
75 }
76
77 PFN_TYPE
78 NTAPI
79 MmGetLRUFirstUserPage(VOID)
80 {
81 ULONG Position;
82 KIRQL OldIrql;
83
84 /* Find the first user page */
85 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
86 Position = RtlFindSetBits(&MiUserPfnBitMap, 1, 0);
87 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
88 if (Position == 0xFFFFFFFF) return 0;
89
90 /* Return it */
91 return Position;
92 }
93
94 VOID
95 NTAPI
96 MmInsertLRULastUserPage(PFN_TYPE Pfn)
97 {
98 KIRQL OldIrql;
99
100 /* Set the page as a user page */
101 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
102 RtlSetBit(&MiUserPfnBitMap, Pfn);
103 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
104 }
105
106 PFN_TYPE
107 NTAPI
108 MmGetLRUNextUserPage(PFN_TYPE PreviousPfn)
109 {
110 ULONG Position;
111 KIRQL OldIrql;
112
113 /* Find the next user page */
114 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
115 Position = RtlFindSetBits(&MiUserPfnBitMap, 1, PreviousPfn + 1);
116 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
117 if (Position == 0xFFFFFFFF) return 0;
118
119 /* Return it */
120 return Position;
121 }
122
123 VOID
124 NTAPI
125 MmRemoveLRUUserPage(PFN_TYPE Page)
126 {
127 /* Unset the page as a user page */
128 RtlClearBit(&MiUserPfnBitMap, Page);
129 }
130
131 BOOLEAN
132 NTAPI
133 MiIsPfnFree(IN PMMPFN Pfn1)
134 {
135 /* Must be a free or zero page, with no references, linked */
136 return ((Pfn1->u3.e1.PageLocation <= StandbyPageList) &&
137 (Pfn1->u1.Flink) &&
138 (Pfn1->u2.Blink) &&
139 !(Pfn1->u3.e2.ReferenceCount));
140 }
141
142 BOOLEAN
143 NTAPI
144 MiIsPfnInUse(IN PMMPFN Pfn1)
145 {
146 /* Standby list or higher, unlinked, and with references */
147 return !MiIsPfnFree(Pfn1);
148 }
149
150 PMDL
151 NTAPI
152 MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress,
153 IN PHYSICAL_ADDRESS HighAddress,
154 IN PHYSICAL_ADDRESS SkipBytes,
155 IN SIZE_T TotalBytes,
156 IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute,
157 IN ULONG MdlFlags)
158 {
159 PMDL Mdl;
160 PFN_NUMBER PageCount, LowPage, HighPage, SkipPages, PagesFound = 0, Page;
161 PPFN_NUMBER MdlPage, LastMdlPage;
162 KIRQL OldIrql;
163 PPHYSICAL_PAGE Pfn1;
164 INT LookForZeroedPages;
165 ASSERT (KeGetCurrentIrql() <= APC_LEVEL);
166
167 //
168 // Convert the low address into a PFN
169 //
170 LowPage = (PFN_NUMBER)(LowAddress.QuadPart >> PAGE_SHIFT);
171
172 //
173 // Convert, and normalize, the high address into a PFN
174 //
175 HighPage = (PFN_NUMBER)(HighAddress.QuadPart >> PAGE_SHIFT);
176 if (HighPage > MmHighestPhysicalPage) HighPage = MmHighestPhysicalPage;
177
178 //
179 // Validate skipbytes and convert them into pages
180 //
181 if (BYTE_OFFSET(SkipBytes.LowPart)) return NULL;
182 SkipPages = (PFN_NUMBER)(SkipBytes.QuadPart >> PAGE_SHIFT);
183
184 //
185 // Now compute the number of pages the MDL will cover
186 //
187 PageCount = (PFN_NUMBER)ADDRESS_AND_SIZE_TO_SPAN_PAGES(0, TotalBytes);
188 do
189 {
190 //
191 // Try creating an MDL for these many pages
192 //
193 Mdl = MmCreateMdl(NULL, NULL, PageCount << PAGE_SHIFT);
194 if (Mdl) break;
195
196 //
197 // This function is not required to return the amount of pages requested
198 // In fact, it can return as little as 1 page, and callers are supposed
199 // to deal with this scenario. So re-attempt the allocation with less
200 // pages than before, and see if it worked this time.
201 //
202 PageCount -= (PageCount >> 4);
203 } while (PageCount);
204
205 //
206 // Wow, not even a single page was around!
207 //
208 if (!Mdl) return NULL;
209
210 //
211 // This is where the page array starts....
212 //
213 MdlPage = (PPFN_NUMBER)(Mdl + 1);
214
215 //
216 // Lock the PFN database
217 //
218 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
219
220 //
221 // Are we looking for any pages, without discriminating?
222 //
223 if ((LowPage == 0) && (HighPage == MmHighestPhysicalPage))
224 {
225 //
226 // Well then, let's go shopping
227 //
228 while (PagesFound < PageCount)
229 {
230 //
231 // Do we have zeroed pages?
232 //
233 if (MmZeroedPageListHead.Total)
234 {
235 //
236 // Grab a zero page
237 //
238 Pfn1 = MiRemoveHeadList(&MmZeroedPageListHead);
239 }
240 else if (MmFreePageListHead.Total)
241 {
242 //
243 // Nope, grab an unzeroed page
244 //
245 Pfn1 = MiRemoveHeadList(&MmFreePageListHead);
246 }
247 else
248 {
249 //
250 // This is not good... hopefully we have at least SOME pages
251 //
252 ASSERT(PagesFound);
253 break;
254 }
255
256 //
257 // Make sure it's really free
258 //
259 ASSERT(MiIsPfnInUse(Pfn1) == FALSE);
260 ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
261
262 //
263 // Allocate it and mark it
264 //
265 Pfn1->u3.e1.StartOfAllocation = 1;
266 Pfn1->u3.e1.EndOfAllocation = 1;
267 Pfn1->u3.e2.ReferenceCount = 1;
268
269 //
270 // Decrease available pages
271 //
272 MmAvailablePages--;
273
274 //
275 // Save it into the MDL
276 //
277 *MdlPage++ = MiGetPfnEntryIndex(Pfn1);
278 PagesFound++;
279 }
280 }
281 else
282 {
283 //
284 // You want specific range of pages. We'll do this in two runs
285 //
286 for (LookForZeroedPages = 1; LookForZeroedPages >= 0; LookForZeroedPages--)
287 {
288 //
289 // Scan the range you specified
290 //
291 for (Page = LowPage; Page < HighPage; Page++)
292 {
293 //
294 // Get the PFN entry for this page
295 //
296 Pfn1 = MiGetPfnEntry(Page);
297 ASSERT(Pfn1);
298
299 //
300 // Make sure it's free and if this is our first pass, zeroed
301 //
302 if (MiIsPfnInUse(Pfn1)) continue;
303 if ((Pfn1->u3.e1.PageLocation == ZeroedPageList) != LookForZeroedPages) continue;
304
305 //
306 // Sanity checks
307 //
308 ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
309
310 //
311 // Now setup the page and mark it
312 //
313 Pfn1->u3.e2.ReferenceCount = 1;
314 Pfn1->u3.e1.StartOfAllocation = 1;
315 Pfn1->u3.e1.EndOfAllocation = 1;
316
317 //
318 // Decrease available pages
319 //
320 MmAvailablePages--;
321
322 //
323 // Save this page into the MDL
324 //
325 *MdlPage++ = Page;
326 if (++PagesFound == PageCount) break;
327 }
328
329 //
330 // If the first pass was enough, don't keep going, otherwise, go again
331 //
332 if (PagesFound == PageCount) break;
333 }
334 }
335
336 //
337 // Now release the PFN count
338 //
339 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
340
341 //
342 // We might've found less pages, but not more ;-)
343 //
344 if (PagesFound != PageCount) ASSERT(PagesFound < PageCount);
345 if (!PagesFound)
346 {
347 //
348 // If we didn' tfind any pages at all, fail
349 //
350 DPRINT1("NO MDL PAGES!\n");
351 ExFreePool(Mdl);
352 return NULL;
353 }
354
355 //
356 // Write out how many pages we found
357 //
358 Mdl->ByteCount = (ULONG)(PagesFound << PAGE_SHIFT);
359
360 //
361 // Terminate the MDL array if there's certain missing pages
362 //
363 if (PagesFound != PageCount) *MdlPage = -1;
364
365 //
366 // Now go back and loop over all the MDL pages
367 //
368 MdlPage = (PPFN_NUMBER)(Mdl + 1);
369 LastMdlPage = MdlPage + PagesFound;
370 while (MdlPage < LastMdlPage)
371 {
372 //
373 // Check if we've reached the end
374 //
375 Page = *MdlPage++;
376 if (Page == (PFN_NUMBER)-1) break;
377
378 //
379 // Get the PFN entry for the page and check if we should zero it out
380 //
381 Pfn1 = MiGetPfnEntry(Page);
382 ASSERT(Pfn1);
383 if (Pfn1->u3.e1.PageLocation != ZeroedPageList) MiZeroPage(Page);
384 Pfn1->u3.e1.PageLocation = ActiveAndValid;
385 }
386
387 //
388 // We're done, mark the pages as locked (should we lock them, though???)
389 //
390 Mdl->Process = NULL;
391 Mdl->MdlFlags |= MDL_PAGES_LOCKED;
392 return Mdl;
393 }
394
395 VOID
396 NTAPI
397 MmDumpPfnDatabase(VOID)
398 {
399 ULONG i;
400 PPHYSICAL_PAGE Pfn1;
401 PCHAR State = "????", Type = "Unknown";
402 KIRQL OldIrql;
403 ULONG Totals[5] = {0}, FreePages = 0;
404
405 KeRaiseIrql(HIGH_LEVEL, &OldIrql);
406
407 //
408 // Loop the PFN database
409 //
410 for (i = 0; i <= MmHighestPhysicalPage; i++)
411 {
412 Pfn1 = MiGetPfnEntry(i);
413 if (!Pfn1) continue;
414
415 //
416 // Get the type
417 //
418 if (MiIsPfnInUse(Pfn1))
419 {
420 State = "Used";
421 }
422 else
423 {
424 State = "Free";
425 Type = "Free";
426 FreePages++;
427 break;
428 }
429
430 //
431 // Pretty-print the page
432 //
433 DbgPrint("0x%08p:\t%04s\t%20s\t(%02d) [%08p])\n",
434 i << PAGE_SHIFT,
435 State,
436 Type,
437 Pfn1->u3.e2.ReferenceCount,
438 Pfn1->RmapListHead);
439 }
440
441 DbgPrint("Nonpaged Pool: %d pages\t[%d KB]\n", Totals[MC_NPPOOL], (Totals[MC_NPPOOL] << PAGE_SHIFT) / 1024);
442 DbgPrint("Paged Pool: %d pages\t[%d KB]\n", Totals[MC_PPOOL], (Totals[MC_PPOOL] << PAGE_SHIFT) / 1024);
443 DbgPrint("File System Cache: %d pages\t[%d KB]\n", Totals[MC_CACHE], (Totals[MC_CACHE] << PAGE_SHIFT) / 1024);
444 DbgPrint("Process Working Set: %d pages\t[%d KB]\n", Totals[MC_USER], (Totals[MC_USER] << PAGE_SHIFT) / 1024);
445 DbgPrint("System: %d pages\t[%d KB]\n", Totals[MC_SYSTEM], (Totals[MC_SYSTEM] << PAGE_SHIFT) / 1024);
446 DbgPrint("Free: %d pages\t[%d KB]\n", FreePages, (FreePages << PAGE_SHIFT) / 1024);
447
448 KeLowerIrql(OldIrql);
449 }
450
451 VOID
452 NTAPI
453 MmInitializePageList(VOID)
454 {
455 ULONG i;
456 PHYSICAL_PAGE UsedPage;
457 PMEMORY_ALLOCATION_DESCRIPTOR Md;
458 PLIST_ENTRY NextEntry;
459 ULONG NrSystemPages = 0;
460 KIRQL OldIrql;
461
462 /* This is what a used page looks like */
463 RtlZeroMemory(&UsedPage, sizeof(UsedPage));
464 UsedPage.u3.e1.PageLocation = ActiveAndValid;
465 UsedPage.u3.e2.ReferenceCount = 1;
466
467 /* Lock PFN database */
468 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
469
470 /* Loop the memory descriptors */
471 for (NextEntry = KeLoaderBlock->MemoryDescriptorListHead.Flink;
472 NextEntry != &KeLoaderBlock->MemoryDescriptorListHead;
473 NextEntry = NextEntry->Flink)
474 {
475 /* Get the descriptor */
476 Md = CONTAINING_RECORD(NextEntry,
477 MEMORY_ALLOCATION_DESCRIPTOR,
478 ListEntry);
479
480 /* Skip bad memory */
481 if ((Md->MemoryType == LoaderFirmwarePermanent) ||
482 (Md->MemoryType == LoaderBBTMemory) ||
483 (Md->MemoryType == LoaderSpecialMemory) ||
484 (Md->MemoryType == LoaderBad))
485 {
486 //
487 // We do not build PFN entries for this
488 //
489 continue;
490 }
491 else if ((Md->MemoryType == LoaderFree) ||
492 (Md->MemoryType == LoaderLoadedProgram) ||
493 (Md->MemoryType == LoaderFirmwareTemporary) ||
494 (Md->MemoryType == LoaderOsloaderStack))
495 {
496 /* Loop every page part of the block */
497 for (i = 0; i < Md->PageCount; i++)
498 {
499 /* Mark it as a free page */
500 MmPfnDatabase[0][Md->BasePage + i].u3.e1.PageLocation = FreePageList;
501 MiInsertInListTail(&MmFreePageListHead,
502 &MmPfnDatabase[0][Md->BasePage + i]);
503 MmAvailablePages++;
504 }
505 }
506 else
507 {
508 /* Loop every page part of the block */
509 for (i = 0; i < Md->PageCount; i++)
510 {
511 /* Everything else is used memory */
512 MmPfnDatabase[0][Md->BasePage + i] = UsedPage;
513 NrSystemPages++;
514 }
515 }
516 }
517
518 /* Finally handle the pages describing the PFN database themselves */
519 for (i = MxOldFreeDescriptor.BasePage; i < MxFreeDescriptor->BasePage; i++)
520 {
521 /* Mark it as used kernel memory */
522 MmPfnDatabase[0][i] = UsedPage;
523 NrSystemPages++;
524 }
525
526 /* Release the PFN database lock */
527 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
528
529 KeInitializeEvent(&ZeroPageThreadEvent, NotificationEvent, TRUE);
530 DPRINT("Pages: %x %x\n", MmAvailablePages, NrSystemPages);
531 MmInitializeBalancer(MmAvailablePages, NrSystemPages);
532 }
533
534 VOID
535 NTAPI
536 MmSetRmapListHeadPage(PFN_TYPE Pfn, struct _MM_RMAP_ENTRY* ListHead)
537 {
538 KIRQL oldIrql;
539
540 oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
541 MiGetPfnEntry(Pfn)->RmapListHead = (LONG)ListHead;
542 KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql);
543 }
544
545 struct _MM_RMAP_ENTRY*
546 NTAPI
547 MmGetRmapListHeadPage(PFN_TYPE Pfn)
548 {
549 KIRQL oldIrql;
550 struct _MM_RMAP_ENTRY* ListHead;
551
552 oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
553 ListHead = (struct _MM_RMAP_ENTRY*)MiGetPfnEntry(Pfn)->RmapListHead;
554 KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql);
555
556 return(ListHead);
557 }
558
559 VOID
560 NTAPI
561 MmSetSavedSwapEntryPage(PFN_TYPE Pfn, SWAPENTRY SwapEntry)
562 {
563 KIRQL oldIrql;
564
565 oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
566 MiGetPfnEntry(Pfn)->u1.WsIndex = SwapEntry;
567 KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql);
568 }
569
570 SWAPENTRY
571 NTAPI
572 MmGetSavedSwapEntryPage(PFN_TYPE Pfn)
573 {
574 SWAPENTRY SwapEntry;
575 KIRQL oldIrql;
576
577 oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
578 SwapEntry = MiGetPfnEntry(Pfn)->u1.WsIndex;
579 KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql);
580
581 return(SwapEntry);
582 }
583
584 VOID
585 NTAPI
586 MmReferencePage(PFN_TYPE Pfn)
587 {
588 PPHYSICAL_PAGE Page;
589
590 DPRINT("MmReferencePage(PysicalAddress %x)\n", Pfn << PAGE_SHIFT);
591
592 if (Pfn == 0 || Pfn > MmHighestPhysicalPage)
593 {
594 return;
595 }
596
597 Page = MiGetPfnEntry(Pfn);
598 ASSERT(Page);
599
600 Page->u3.e2.ReferenceCount++;
601 }
602
603 ULONG
604 NTAPI
605 MmGetReferenceCountPage(PFN_TYPE Pfn)
606 {
607 KIRQL oldIrql;
608 ULONG RCount;
609 PPHYSICAL_PAGE Page;
610
611 DPRINT("MmGetReferenceCountPage(PhysicalAddress %x)\n", Pfn << PAGE_SHIFT);
612
613 oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
614 Page = MiGetPfnEntry(Pfn);
615 ASSERT(Page);
616
617 RCount = Page->u3.e2.ReferenceCount;
618
619 KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql);
620 return(RCount);
621 }
622
623 BOOLEAN
624 NTAPI
625 MmIsPageInUse(PFN_TYPE Pfn)
626 {
627 return MiIsPfnInUse(MiGetPfnEntry(Pfn));
628 }
629
630 VOID
631 NTAPI
632 MiSetConsumer(IN PFN_TYPE Pfn,
633 IN ULONG Type)
634 {
635 MiGetPfnEntry(Pfn)->u3.e1.PageLocation = ActiveAndValid;
636 }
637
638 VOID
639 NTAPI
640 MmDereferencePage(PFN_TYPE Pfn)
641 {
642 PPHYSICAL_PAGE Page;
643
644 DPRINT("MmDereferencePage(PhysicalAddress %x)\n", Pfn << PAGE_SHIFT);
645
646 Page = MiGetPfnEntry(Pfn);
647 ASSERT(Page);
648
649 Page->u3.e2.ReferenceCount--;
650 if (Page->u3.e2.ReferenceCount == 0)
651 {
652 MmAvailablePages++;
653 Page->u3.e1.PageLocation = FreePageList;
654 MiInsertInListTail(&MmFreePageListHead, Page);
655 if (MmFreePageListHead.Total > 8 && 0 == KeReadStateEvent(&ZeroPageThreadEvent))
656 {
657 KeSetEvent(&ZeroPageThreadEvent, IO_NO_INCREMENT, FALSE);
658 }
659 }
660 }
661
662 PFN_TYPE
663 NTAPI
664 MmAllocPage(ULONG Type)
665 {
666 PFN_TYPE PfnOffset;
667 PPHYSICAL_PAGE PageDescriptor;
668 BOOLEAN NeedClear = FALSE;
669
670 DPRINT("MmAllocPage()\n");
671
672 if (MmZeroedPageListHead.Total == 0)
673 {
674 if (MmFreePageListHead.Total == 0)
675 {
676 /* Check if this allocation is for the PFN DB itself */
677 if (MmNumberOfPhysicalPages == 0)
678 {
679 ASSERT(FALSE);
680 }
681
682 DPRINT1("MmAllocPage(): Out of memory\n");
683 return 0;
684 }
685 PageDescriptor = MiRemoveHeadList(&MmFreePageListHead);
686
687 NeedClear = TRUE;
688 }
689 else
690 {
691 PageDescriptor = MiRemoveHeadList(&MmZeroedPageListHead);
692 }
693
694 PageDescriptor->u3.e2.ReferenceCount = 1;
695
696 MmAvailablePages--;
697
698 PfnOffset = PageDescriptor - MmPfnDatabase[0];
699 if ((NeedClear) && (Type != MC_SYSTEM))
700 {
701 MiZeroPage(PfnOffset);
702 }
703
704 PageDescriptor->u3.e1.PageLocation = ActiveAndValid;
705 return PfnOffset;
706 }
707
708 NTSTATUS
709 NTAPI
710 MiZeroPage(PFN_TYPE Page)
711 {
712 KIRQL Irql;
713 PVOID TempAddress;
714
715 Irql = KeRaiseIrqlToDpcLevel();
716 TempAddress = MiMapPageToZeroInHyperSpace(Page);
717 if (TempAddress == NULL)
718 {
719 return(STATUS_NO_MEMORY);
720 }
721 memset(TempAddress, 0, PAGE_SIZE);
722 MiUnmapPagesInZeroSpace(TempAddress, 1);
723 KeLowerIrql(Irql);
724 return(STATUS_SUCCESS);
725 }
726
727 NTSTATUS
728 NTAPI
729 MmZeroPageThreadMain(PVOID Ignored)
730 {
731 NTSTATUS Status;
732 KIRQL oldIrql;
733 PPHYSICAL_PAGE PageDescriptor;
734 PFN_TYPE Pfn;
735 ULONG Count;
736
737 /* Free initial kernel memory */
738 //MiFreeInitMemory();
739
740 /* Set our priority to 0 */
741 KeGetCurrentThread()->BasePriority = 0;
742 KeSetPriorityThread(KeGetCurrentThread(), 0);
743
744 while(1)
745 {
746 Status = KeWaitForSingleObject(&ZeroPageThreadEvent,
747 0,
748 KernelMode,
749 FALSE,
750 NULL);
751
752 if (ZeroPageThreadShouldTerminate)
753 {
754 DPRINT1("ZeroPageThread: Terminating\n");
755 return STATUS_SUCCESS;
756 }
757 Count = 0;
758 oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
759 while (MmFreePageListHead.Total)
760 {
761 PageDescriptor = MiRemoveHeadList(&MmFreePageListHead);
762 /* We set the page to used, because MmCreateVirtualMapping failed with unused pages */
763 KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql);
764 Pfn = PageDescriptor - MmPfnDatabase[0];
765 Status = MiZeroPage(Pfn);
766
767 oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
768 if (NT_SUCCESS(Status))
769 {
770 MiInsertZeroListAtBack(Pfn);
771 Count++;
772 }
773 else
774 {
775 MiInsertInListTail(&MmFreePageListHead, PageDescriptor);
776 PageDescriptor->u3.e1.PageLocation = FreePageList;
777 }
778
779 }
780 DPRINT("Zeroed %d pages.\n", Count);
781 KeResetEvent(&ZeroPageThreadEvent);
782 KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql);
783 }
784
785 return STATUS_SUCCESS;
786 }
787
788 /* EOF */