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
7 * PROGRAMMERS: David Welch (welch@cwcom.net)
11 /* INCLUDES ****************************************************************/
17 #if defined (ALLOC_PRAGMA)
18 #pragma alloc_text(INIT, MmInitializePageList)
21 #define MODULE_INVOLVED_IN_ARM3
22 #include "ARM3/miarm.h"
24 /* TYPES *******************************************************************/
26 #define MM_PHYSICAL_PAGE_FREE (0x1)
27 #define MM_PHYSICAL_PAGE_USED (0x2)
29 /* GLOBALS ****************************************************************/
33 // ReactOS to NT Physical Page Descriptor Entry Legacy Mapping Definitions
37 #define Consumer PageLocation
38 #define Type CacheAttribute
39 #define Zero PrototypePte
40 #define LockCount u3.e1.PageColor
41 #define RmapListHead AweReferenceCount
42 #define SavedSwapEntry u4.EntireFrame
44 #define ReferenceCount u3.ReferenceCount
45 #define RemoveEntryList(x) RemoveEntryList((PLIST_ENTRY)x)
46 #define InsertTailList(x, y) InsertTailList(x, (PLIST_ENTRY)y)
48 #define PHYSICAL_PAGE MMPFN
49 #define PPHYSICAL_PAGE PMMPFN
51 /* The first array contains ReactOS PFNs, the second contains ARM3 PFNs */
52 PPHYSICAL_PAGE MmPfnDatabase
[2];
54 ULONG MmAvailablePages
;
55 ULONG MmResidentAvailablePages
;
57 SIZE_T MmTotalCommitLimit
;
58 SIZE_T MmTotalCommittedPages
;
59 SIZE_T MmSharedCommit
;
60 SIZE_T MmDriverCommit
;
61 SIZE_T MmProcessCommit
;
62 SIZE_T MmPagedPoolCommit
;
63 SIZE_T MmPeakCommitment
;
64 SIZE_T MmtotalCommitLimitMaximum
;
66 MMPFNLIST MmZeroedPageListHead
;
67 MMPFNLIST MmFreePageListHead
;
68 MMPFNLIST MmStandbyPageListHead
;
69 MMPFNLIST MmModifiedPageListHead
;
70 MMPFNLIST MmModifiedNoWritePageListHead
;
72 /* List of pages allocated to the MC_USER Consumer */
73 static LIST_ENTRY UserPageListHead
;
74 /* List of pages zeroed by the ZPW (MmZeroPageThreadMain) */
75 static LIST_ENTRY FreeZeroedPageListHead
;
76 /* List of free pages, filled by MmGetReferenceCountPage and
77 * and MmInitializePageList */
78 static LIST_ENTRY FreeUnzeroedPageListHead
;
80 static KEVENT ZeroPageThreadEvent
;
81 static BOOLEAN ZeroPageThreadShouldTerminate
= FALSE
;
83 static ULONG UnzeroedPageCount
= 0;
85 /* FUNCTIONS *************************************************************/
89 MmGetLRUFirstUserPage(VOID
)
91 PLIST_ENTRY NextListEntry
;
92 PHYSICAL_PAGE
* PageDescriptor
;
95 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
96 NextListEntry
= UserPageListHead
.Flink
;
97 if (NextListEntry
== &UserPageListHead
)
99 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
102 PageDescriptor
= CONTAINING_RECORD(NextListEntry
, PHYSICAL_PAGE
, ListEntry
);
103 ASSERT_PFN(PageDescriptor
);
104 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
105 return PageDescriptor
- MmPfnDatabase
[0];
110 MmInsertLRULastUserPage(PFN_TYPE Pfn
)
115 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
116 Page
= MiGetPfnEntry(Pfn
);
118 ASSERT(Page
->Flags
.Type
== MM_PHYSICAL_PAGE_USED
);
119 ASSERT(Page
->Flags
.Consumer
== MC_USER
);
120 InsertTailList(&UserPageListHead
, &Page
->ListEntry
);
121 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
126 MmGetLRUNextUserPage(PFN_TYPE PreviousPfn
)
128 PLIST_ENTRY NextListEntry
;
129 PHYSICAL_PAGE
* PageDescriptor
;
133 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
134 Page
= MiGetPfnEntry(PreviousPfn
);
136 ASSERT(Page
->Flags
.Type
== MM_PHYSICAL_PAGE_USED
);
137 ASSERT(Page
->Flags
.Consumer
== MC_USER
);
138 NextListEntry
= (PLIST_ENTRY
)Page
->ListEntry
.Flink
;
139 if (NextListEntry
== &UserPageListHead
)
141 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
144 PageDescriptor
= CONTAINING_RECORD(NextListEntry
, PHYSICAL_PAGE
, ListEntry
);
145 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
146 return PageDescriptor
- MmPfnDatabase
[0];
151 MmRemoveLRUUserPage(PFN_TYPE Page
)
153 RemoveEntryList(&MiGetPfnEntry(Page
)->ListEntry
);
158 MiFindContiguousPages(IN PFN_NUMBER LowestPfn
,
159 IN PFN_NUMBER HighestPfn
,
160 IN PFN_NUMBER BoundaryPfn
,
161 IN PFN_NUMBER SizeInPages
,
162 IN MEMORY_CACHING_TYPE CacheType
)
164 PFN_NUMBER Page
, PageCount
, LastPage
, Length
, BoundaryMask
;
169 ASSERT(SizeInPages
!= 0);
172 // Convert the boundary PFN into an alignment mask
174 BoundaryMask
= ~(BoundaryPfn
- 1);
177 // Loop all the physical memory blocks
182 // Capture the base page and length of this memory block
184 Page
= MmPhysicalMemoryBlock
->Run
[i
].BasePage
;
185 PageCount
= MmPhysicalMemoryBlock
->Run
[i
].PageCount
;
188 // Check how far this memory block will go
190 LastPage
= Page
+ PageCount
;
193 // Trim it down to only the PFNs we're actually interested in
195 if ((LastPage
- 1) > HighestPfn
) LastPage
= HighestPfn
+ 1;
196 if (Page
< LowestPfn
) Page
= LowestPfn
;
199 // Skip this run if it's empty or fails to contain all the pages we need
201 if (!(PageCount
) || ((Page
+ SizeInPages
) > LastPage
)) continue;
204 // Now scan all the relevant PFNs in this run
207 for (Pfn1
= MiGetPfnEntry(Page
); Page
< LastPage
; Page
++, Pfn1
++)
210 // If this PFN is in use, ignore it
212 if (Pfn1
->Flags
.Type
!= MM_PHYSICAL_PAGE_FREE
) continue;
215 // If we haven't chosen a start PFN yet and the caller specified an
216 // alignment, make sure the page matches the alignment restriction
218 if ((!(Length
) && (BoundaryPfn
)) &&
219 (((Page
^ (Page
+ SizeInPages
- 1)) & BoundaryMask
)))
222 // It does not, so bail out
228 // Increase the number of valid pages, and check if we have enough
230 if (++Length
== SizeInPages
)
233 // It appears we've amassed enough legitimate pages, rollback
235 Pfn1
-= (Length
- 1);
236 Page
-= (Length
- 1);
239 // Acquire the PFN lock
241 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
245 // Things might've changed for us. Is the page still free?
247 if (Pfn1
->Flags
.Type
!= MM_PHYSICAL_PAGE_FREE
) break;
250 // So far so good. Is this the last confirmed valid page?
255 // Sanity check that we didn't go out of bounds
257 ASSERT(i
!= MmPhysicalMemoryBlock
->NumberOfRuns
);
260 // Loop until all PFN entries have been processed
262 EndPfn
= Pfn1
- SizeInPages
+ 1;
266 // If this was an unzeroed page, there are now less
268 if (Pfn1
->Flags
.Zero
== 0) UnzeroedPageCount
--;
271 // One less free page
276 // This PFN is now a used page, set it up
278 RemoveEntryList(&Pfn1
->ListEntry
);
279 Pfn1
->Flags
.Type
= MM_PHYSICAL_PAGE_USED
;
280 Pfn1
->Flags
.Consumer
= MC_NPPOOL
;
281 Pfn1
->ReferenceCount
= 1;
283 Pfn1
->SavedSwapEntry
= 0;
286 // Check if it was already zeroed
288 if (Pfn1
->Flags
.Zero
== 0)
291 // It wasn't, so zero it
293 MiZeroPage(MiGetPfnEntryIndex(Pfn1
));
297 // Check if this is the last PFN, otherwise go on
299 if (Pfn1
== EndPfn
) break;
304 // Mark the first and last PFN so we can find them later
306 Pfn1
->Flags
.StartOfAllocation
= 1;
307 (Pfn1
+ SizeInPages
- 1)->Flags
.EndOfAllocation
= 1;
310 // Now it's safe to let go of the PFN lock
312 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
315 // Quick sanity check that the last PFN is consistent
317 EndPfn
= Pfn1
+ SizeInPages
;
318 ASSERT(EndPfn
== MiGetPfnEntry(Page
+ 1));
321 // Compute the first page, and make sure it's consistent
323 Page
-= SizeInPages
- 1;
324 ASSERT(Pfn1
== MiGetPfnEntry(Page
));
330 // Keep going. The purpose of this loop is to reconfirm that
331 // after acquiring the PFN lock these pages are still usable
338 // If we got here, something changed while we hadn't acquired
339 // the PFN lock yet, so we'll have to restart
341 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
345 } while (++i
!= MmPhysicalMemoryBlock
->NumberOfRuns
);
348 // And if we get here, it means no suitable physical memory runs were found
355 MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress
,
356 IN PHYSICAL_ADDRESS HighAddress
,
357 IN PHYSICAL_ADDRESS SkipBytes
,
358 IN SIZE_T TotalBytes
,
359 IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute
,
363 PFN_NUMBER PageCount
, LowPage
, HighPage
, SkipPages
, PagesFound
= 0, Page
;
364 PPFN_NUMBER MdlPage
, LastMdlPage
;
366 PLIST_ENTRY ListEntry
;
368 INT LookForZeroedPages
;
369 ASSERT (KeGetCurrentIrql() <= APC_LEVEL
);
372 // Convert the low address into a PFN
374 LowPage
= (PFN_NUMBER
)(LowAddress
.QuadPart
>> PAGE_SHIFT
);
377 // Convert, and normalize, the high address into a PFN
379 HighPage
= (PFN_NUMBER
)(HighAddress
.QuadPart
>> PAGE_SHIFT
);
380 if (HighPage
> MmHighestPhysicalPage
) HighPage
= MmHighestPhysicalPage
;
383 // Validate skipbytes and convert them into pages
385 if (BYTE_OFFSET(SkipBytes
.LowPart
)) return NULL
;
386 SkipPages
= (PFN_NUMBER
)(SkipBytes
.QuadPart
>> PAGE_SHIFT
);
389 // Now compute the number of pages the MDL will cover
391 PageCount
= (PFN_NUMBER
)ADDRESS_AND_SIZE_TO_SPAN_PAGES(0, TotalBytes
);
395 // Try creating an MDL for these many pages
397 Mdl
= MmCreateMdl(NULL
, NULL
, PageCount
<< PAGE_SHIFT
);
401 // This function is not required to return the amount of pages requested
402 // In fact, it can return as little as 1 page, and callers are supposed
403 // to deal with this scenario. So re-attempt the allocation with less
404 // pages than before, and see if it worked this time.
406 PageCount
-= (PageCount
>> 4);
410 // Wow, not even a single page was around!
412 if (!Mdl
) return NULL
;
415 // This is where the page array starts....
417 MdlPage
= (PPFN_NUMBER
)(Mdl
+ 1);
420 // Lock the PFN database
422 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
425 // Are we looking for any pages, without discriminating?
427 if ((LowPage
== 0) && (HighPage
== MmHighestPhysicalPage
))
430 // Well then, let's go shopping
432 while (PagesFound
< PageCount
)
435 // Do we have zeroed pages?
437 if (!IsListEmpty(&FreeZeroedPageListHead
))
442 ListEntry
= RemoveTailList(&FreeZeroedPageListHead
);
444 else if (!IsListEmpty(&FreeUnzeroedPageListHead
))
447 // Nope, grab an unzeroed page
449 ListEntry
= RemoveTailList(&FreeUnzeroedPageListHead
);
455 // This is not good... hopefully we have at least SOME pages
462 // Get the PFN entry for this page
464 Pfn1
= CONTAINING_RECORD(ListEntry
, PHYSICAL_PAGE
, ListEntry
);
467 // Make sure it's really free
469 ASSERT(Pfn1
->Flags
.Type
== MM_PHYSICAL_PAGE_FREE
);
470 ASSERT(Pfn1
->ReferenceCount
== 0);
473 // Allocate it and mark it
475 Pfn1
->Flags
.Type
= MM_PHYSICAL_PAGE_USED
;
476 Pfn1
->Flags
.Consumer
= MC_NPPOOL
;
477 Pfn1
->Flags
.StartOfAllocation
= 1;
478 Pfn1
->Flags
.EndOfAllocation
= 1;
479 Pfn1
->ReferenceCount
= 1;
481 Pfn1
->SavedSwapEntry
= 0;
484 // Decrease available pages
489 // Save it into the MDL
491 *MdlPage
++ = MiGetPfnEntryIndex(Pfn1
);
498 // You want specific range of pages. We'll do this in two runs
500 for (LookForZeroedPages
= 1; LookForZeroedPages
>= 0; LookForZeroedPages
--)
503 // Scan the range you specified
505 for (Page
= LowPage
; Page
< HighPage
; Page
++)
508 // Get the PFN entry for this page
510 Pfn1
= MiGetPfnEntry(Page
);
514 // Make sure it's free and if this is our first pass, zeroed
516 if (Pfn1
->Flags
.Type
!= MM_PHYSICAL_PAGE_FREE
) continue;
517 if (Pfn1
->Flags
.Zero
!= LookForZeroedPages
) continue;
522 ASSERT(Pfn1
->ReferenceCount
== 0);
525 // Now setup the page and mark it
527 Pfn1
->Flags
.Type
= MM_PHYSICAL_PAGE_USED
;
528 Pfn1
->Flags
.Consumer
= MC_NPPOOL
;
529 Pfn1
->ReferenceCount
= 1;
530 Pfn1
->Flags
.StartOfAllocation
= 1;
531 Pfn1
->Flags
.EndOfAllocation
= 1;
533 Pfn1
->SavedSwapEntry
= 0;
536 // If this page was unzeroed, we've consumed such a page
538 if (!Pfn1
->Flags
.Zero
) UnzeroedPageCount
--;
541 // Decrease available pages
546 // Save this page into the MDL
549 if (++PagesFound
== PageCount
) break;
553 // If the first pass was enough, don't keep going, otherwise, go again
555 if (PagesFound
== PageCount
) break;
560 // Now release the PFN count
562 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
565 // We might've found less pages, but not more ;-)
567 if (PagesFound
!= PageCount
) ASSERT(PagesFound
< PageCount
);
571 // If we didn' tfind any pages at all, fail
573 DPRINT1("NO MDL PAGES!\n");
579 // Write out how many pages we found
581 Mdl
->ByteCount
= (ULONG
)(PagesFound
<< PAGE_SHIFT
);
584 // Terminate the MDL array if there's certain missing pages
586 if (PagesFound
!= PageCount
) *MdlPage
= -1;
589 // Now go back and loop over all the MDL pages
591 MdlPage
= (PPFN_NUMBER
)(Mdl
+ 1);
592 LastMdlPage
= MdlPage
+ PagesFound
;
593 while (MdlPage
< LastMdlPage
)
596 // Check if we've reached the end
599 if (Page
== (PFN_NUMBER
)-1) break;
602 // Get the PFN entry for the page and check if we should zero it out
604 Pfn1
= MiGetPfnEntry(Page
);
606 if (Pfn1
->Flags
.Zero
== 0) MiZeroPage(Page
);
610 // We're done, mark the pages as locked (should we lock them, though???)
613 Mdl
->MdlFlags
|= MDL_PAGES_LOCKED
;
619 MmDumpPfnDatabase(VOID
)
623 PCHAR State
= "????", Consumer
= "Unknown";
625 ULONG Totals
[5] = {0}, FreePages
= 0;
627 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
630 // Loop the PFN database
632 for (i
= 0; i
<= MmHighestPhysicalPage
; i
++)
634 Pfn1
= MiGetPfnEntry(i
);
640 switch (Pfn1
->Flags
.Consumer
)
644 Consumer
= "Nonpaged Pool";
649 Consumer
= "Paged Pool";
654 Consumer
= "File System Cache";
659 Consumer
= "Process Working Set";
671 switch (Pfn1
->Flags
.Type
)
673 case MM_PHYSICAL_PAGE_USED
:
676 Totals
[Pfn1
->Flags
.Consumer
]++;
679 case MM_PHYSICAL_PAGE_FREE
:
688 // Pretty-print the page
690 DbgPrint("0x%08p:\t%04s\t%20s\t(%02d.%02d) [%08p])\n",
694 Pfn1
->ReferenceCount
,
699 DbgPrint("Nonpaged Pool: %d pages\t[%d KB]\n", Totals
[MC_NPPOOL
], (Totals
[MC_NPPOOL
] << PAGE_SHIFT
) / 1024);
700 DbgPrint("Paged Pool: %d pages\t[%d KB]\n", Totals
[MC_PPOOL
], (Totals
[MC_PPOOL
] << PAGE_SHIFT
) / 1024);
701 DbgPrint("File System Cache: %d pages\t[%d KB]\n", Totals
[MC_CACHE
], (Totals
[MC_CACHE
] << PAGE_SHIFT
) / 1024);
702 DbgPrint("Process Working Set: %d pages\t[%d KB]\n", Totals
[MC_USER
], (Totals
[MC_USER
] << PAGE_SHIFT
) / 1024);
703 DbgPrint("System: %d pages\t[%d KB]\n", Totals
[MC_SYSTEM
], (Totals
[MC_SYSTEM
] << PAGE_SHIFT
) / 1024);
704 DbgPrint("Free: %d pages\t[%d KB]\n", FreePages
, (FreePages
<< PAGE_SHIFT
) / 1024);
706 KeLowerIrql(OldIrql
);
711 MmInitializePageList(VOID
)
714 PHYSICAL_PAGE UsedPage
;
715 PMEMORY_ALLOCATION_DESCRIPTOR Md
;
716 PLIST_ENTRY NextEntry
;
717 ULONG NrSystemPages
= 0;
719 /* Initialize the page lists */
720 InitializeListHead(&UserPageListHead
);
721 InitializeListHead(&FreeUnzeroedPageListHead
);
722 InitializeListHead(&FreeZeroedPageListHead
);
724 /* This is what a used page looks like */
725 RtlZeroMemory(&UsedPage
, sizeof(UsedPage
));
726 UsedPage
.Flags
.Type
= MM_PHYSICAL_PAGE_USED
;
727 UsedPage
.Flags
.Consumer
= MC_NPPOOL
;
728 UsedPage
.ReferenceCount
= 1;
730 /* Loop the memory descriptors */
731 for (NextEntry
= KeLoaderBlock
->MemoryDescriptorListHead
.Flink
;
732 NextEntry
!= &KeLoaderBlock
->MemoryDescriptorListHead
;
733 NextEntry
= NextEntry
->Flink
)
736 /* Get the descriptor */
737 Md
= CONTAINING_RECORD(NextEntry
,
738 MEMORY_ALLOCATION_DESCRIPTOR
,
742 /* Skip bad memory */
743 if ((Md
->MemoryType
== LoaderFirmwarePermanent
) ||
744 (Md
->MemoryType
== LoaderBBTMemory
) ||
745 (Md
->MemoryType
== LoaderSpecialMemory
) ||
746 (Md
->MemoryType
== LoaderBad
))
749 // We do not build PFN entries for this
753 else if ((Md
->MemoryType
== LoaderFree
) ||
754 (Md
->MemoryType
== LoaderLoadedProgram
) ||
755 (Md
->MemoryType
== LoaderFirmwareTemporary
) ||
756 (Md
->MemoryType
== LoaderOsloaderStack
))
758 /* Loop every page part of the block */
759 for (i
= 0; i
< Md
->PageCount
; i
++)
761 /* Mark it as a free page */
762 MmPfnDatabase
[0][Md
->BasePage
+ i
].Flags
.Type
= MM_PHYSICAL_PAGE_FREE
;
763 InsertTailList(&FreeUnzeroedPageListHead
,
764 &MmPfnDatabase
[0][Md
->BasePage
+ i
].ListEntry
);
771 /* Loop every page part of the block */
772 for (i
= 0; i
< Md
->PageCount
; i
++)
774 /* Everything else is used memory */
775 MmPfnDatabase
[0][Md
->BasePage
+ i
] = UsedPage
;
781 /* Finally handle the pages describing the PFN database themselves */
782 for (i
= MxOldFreeDescriptor
.BasePage
; i
< MxFreeDescriptor
->BasePage
; i
++)
784 /* Ensure this page was not added previously */
785 ASSERT(MmPfnDatabase
[0][i
].Flags
.Type
== 0);
787 /* Mark it as used kernel memory */
788 MmPfnDatabase
[0][i
] = UsedPage
;
792 KeInitializeEvent(&ZeroPageThreadEvent
, NotificationEvent
, TRUE
);
793 DPRINT("Pages: %x %x\n", MmAvailablePages
, NrSystemPages
);
794 MmInitializeBalancer(MmAvailablePages
, NrSystemPages
);
799 MmSetRmapListHeadPage(PFN_TYPE Pfn
, struct _MM_RMAP_ENTRY
* ListHead
)
803 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
804 MiGetPfnEntry(Pfn
)->RmapListHead
= (LONG
)ListHead
;
805 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
808 struct _MM_RMAP_ENTRY
*
810 MmGetRmapListHeadPage(PFN_TYPE Pfn
)
813 struct _MM_RMAP_ENTRY
* ListHead
;
815 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
816 ListHead
= (struct _MM_RMAP_ENTRY
*)MiGetPfnEntry(Pfn
)->RmapListHead
;
817 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
824 MmSetSavedSwapEntryPage(PFN_TYPE Pfn
, SWAPENTRY SwapEntry
)
828 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
829 MiGetPfnEntry(Pfn
)->SavedSwapEntry
= SwapEntry
;
830 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
835 MmGetSavedSwapEntryPage(PFN_TYPE Pfn
)
840 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
841 SwapEntry
= MiGetPfnEntry(Pfn
)->SavedSwapEntry
;
842 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
849 MmReferencePage(PFN_TYPE Pfn
)
853 DPRINT("MmReferencePage(PysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
855 if (Pfn
== 0 || Pfn
> MmHighestPhysicalPage
)
860 Page
= MiGetPfnEntry(Pfn
);
862 if (Page
->Flags
.Type
!= MM_PHYSICAL_PAGE_USED
)
864 DPRINT1("Referencing non-used page\n");
865 KeBugCheck(MEMORY_MANAGEMENT
);
868 Page
->ReferenceCount
++;
873 MmGetReferenceCountPage(PFN_TYPE Pfn
)
879 DPRINT("MmGetReferenceCountPage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
881 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
882 Page
= MiGetPfnEntry(Pfn
);
884 if (Page
->Flags
.Type
!= MM_PHYSICAL_PAGE_USED
)
886 DPRINT1("Getting reference count for free page\n");
887 KeBugCheck(MEMORY_MANAGEMENT
);
890 RCount
= Page
->ReferenceCount
;
892 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
898 MmIsPageInUse(PFN_TYPE Pfn
)
901 DPRINT("MmIsPageInUse(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
903 return (MiGetPfnEntry(Pfn
)->Flags
.Type
== MM_PHYSICAL_PAGE_USED
);
908 MmDereferencePage(PFN_TYPE Pfn
)
912 DPRINT("MmDereferencePage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
914 Page
= MiGetPfnEntry(Pfn
);
917 if (Page
->Flags
.Type
!= MM_PHYSICAL_PAGE_USED
)
919 DPRINT1("Dereferencing free page\n");
920 KeBugCheck(MEMORY_MANAGEMENT
);
922 if (Page
->ReferenceCount
== 0)
924 DPRINT1("Derefrencing page with reference count 0\n");
925 KeBugCheck(MEMORY_MANAGEMENT
);
928 Page
->ReferenceCount
--;
929 if (Page
->ReferenceCount
== 0)
932 if (Page
->Flags
.Consumer
== MC_USER
) RemoveEntryList(&Page
->ListEntry
);
933 if (Page
->RmapListHead
!= (LONG
)NULL
)
935 DPRINT1("Freeing page with rmap entries.\n");
936 KeBugCheck(MEMORY_MANAGEMENT
);
938 if (Page
->LockCount
> 0)
940 DPRINT1("Freeing locked page\n");
941 KeBugCheck(MEMORY_MANAGEMENT
);
943 if (Page
->SavedSwapEntry
!= 0)
945 DPRINT1("Freeing page with swap entry.\n");
946 KeBugCheck(MEMORY_MANAGEMENT
);
948 if (Page
->Flags
.Type
!= MM_PHYSICAL_PAGE_USED
)
950 DPRINT1("Freeing page with flags %x\n",
952 KeBugCheck(MEMORY_MANAGEMENT
);
954 Page
->Flags
.Type
= MM_PHYSICAL_PAGE_FREE
;
955 Page
->Flags
.Consumer
= MC_MAXIMUM
;
956 InsertTailList(&FreeUnzeroedPageListHead
,
959 if (UnzeroedPageCount
> 8 && 0 == KeReadStateEvent(&ZeroPageThreadEvent
))
961 KeSetEvent(&ZeroPageThreadEvent
, IO_NO_INCREMENT
, FALSE
);
968 MmGetLockCountPage(PFN_TYPE Pfn
)
971 ULONG CurrentLockCount
;
974 DPRINT("MmGetLockCountPage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
976 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
978 Page
= MiGetPfnEntry(Pfn
);
980 if (Page
->Flags
.Type
!= MM_PHYSICAL_PAGE_USED
)
982 DPRINT1("Getting lock count for free page\n");
983 KeBugCheck(MEMORY_MANAGEMENT
);
986 CurrentLockCount
= Page
->LockCount
;
987 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
989 return(CurrentLockCount
);
994 MmLockPage(PFN_TYPE Pfn
)
998 DPRINT("MmLockPage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
1000 Page
= MiGetPfnEntry(Pfn
);
1002 if (Page
->Flags
.Type
!= MM_PHYSICAL_PAGE_USED
)
1004 DPRINT1("Locking free page\n");
1005 KeBugCheck(MEMORY_MANAGEMENT
);
1013 MmUnlockPage(PFN_TYPE Pfn
)
1015 PPHYSICAL_PAGE Page
;
1017 DPRINT("MmUnlockPage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
1019 Page
= MiGetPfnEntry(Pfn
);
1021 if (Page
->Flags
.Type
!= MM_PHYSICAL_PAGE_USED
)
1023 DPRINT1("Unlocking free page\n");
1024 KeBugCheck(MEMORY_MANAGEMENT
);
1032 MmAllocPage(ULONG Consumer
, SWAPENTRY SwapEntry
)
1035 PLIST_ENTRY ListEntry
;
1036 PPHYSICAL_PAGE PageDescriptor
;
1037 BOOLEAN NeedClear
= FALSE
;
1039 DPRINT("MmAllocPage()\n");
1041 if (IsListEmpty(&FreeZeroedPageListHead
))
1043 if (IsListEmpty(&FreeUnzeroedPageListHead
))
1045 /* Check if this allocation is for the PFN DB itself */
1046 if (MmNumberOfPhysicalPages
== 0)
1051 DPRINT1("MmAllocPage(): Out of memory\n");
1054 ListEntry
= RemoveTailList(&FreeUnzeroedPageListHead
);
1055 UnzeroedPageCount
--;
1057 PageDescriptor
= CONTAINING_RECORD(ListEntry
, PHYSICAL_PAGE
, ListEntry
);
1063 ListEntry
= RemoveTailList(&FreeZeroedPageListHead
);
1065 PageDescriptor
= CONTAINING_RECORD(ListEntry
, PHYSICAL_PAGE
, ListEntry
);
1068 if (PageDescriptor
->Flags
.Type
!= MM_PHYSICAL_PAGE_FREE
)
1070 DPRINT1("Got non-free page from freelist\n");
1071 KeBugCheck(MEMORY_MANAGEMENT
);
1073 if (PageDescriptor
->ReferenceCount
!= 0)
1075 DPRINT1("%d\n", PageDescriptor
->ReferenceCount
);
1076 KeBugCheck(MEMORY_MANAGEMENT
);
1078 PageDescriptor
->Flags
.Type
= MM_PHYSICAL_PAGE_USED
;
1079 PageDescriptor
->Flags
.Consumer
= Consumer
;
1080 PageDescriptor
->ReferenceCount
= 1;
1081 PageDescriptor
->LockCount
= 0;
1082 PageDescriptor
->SavedSwapEntry
= SwapEntry
;
1086 PfnOffset
= PageDescriptor
- MmPfnDatabase
[0];
1087 if ((NeedClear
) && (Consumer
!= MC_SYSTEM
))
1089 MiZeroPage(PfnOffset
);
1096 MiZeroPage(PFN_TYPE Page
)
1101 Irql
= KeRaiseIrqlToDpcLevel();
1102 TempAddress
= MiMapPageToZeroInHyperSpace(Page
);
1103 if (TempAddress
== NULL
)
1105 return(STATUS_NO_MEMORY
);
1107 memset(TempAddress
, 0, PAGE_SIZE
);
1108 MiUnmapPagesInZeroSpace(TempAddress
, 1);
1110 return(STATUS_SUCCESS
);
1115 MmZeroPageThreadMain(PVOID Ignored
)
1119 PLIST_ENTRY ListEntry
;
1120 PPHYSICAL_PAGE PageDescriptor
;
1124 /* Free initial kernel memory */
1125 //MiFreeInitMemory();
1127 /* Set our priority to 0 */
1128 KeGetCurrentThread()->BasePriority
= 0;
1129 KeSetPriorityThread(KeGetCurrentThread(), 0);
1133 Status
= KeWaitForSingleObject(&ZeroPageThreadEvent
,
1138 if (!NT_SUCCESS(Status
))
1140 DPRINT1("ZeroPageThread: Wait failed\n");
1141 KeBugCheck(MEMORY_MANAGEMENT
);
1144 if (ZeroPageThreadShouldTerminate
)
1146 DPRINT1("ZeroPageThread: Terminating\n");
1147 return STATUS_SUCCESS
;
1150 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1151 while (!IsListEmpty(&FreeUnzeroedPageListHead
))
1153 ListEntry
= RemoveTailList(&FreeUnzeroedPageListHead
);
1154 UnzeroedPageCount
--;
1155 PageDescriptor
= CONTAINING_RECORD(ListEntry
, PHYSICAL_PAGE
, ListEntry
);
1156 /* We set the page to used, because MmCreateVirtualMapping failed with unused pages */
1157 PageDescriptor
->Flags
.Type
= MM_PHYSICAL_PAGE_USED
;
1158 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
1159 Pfn
= PageDescriptor
- MmPfnDatabase
[0];
1160 Status
= MiZeroPage(Pfn
);
1162 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1163 PageDescriptor
->Flags
.Zero
= 1;
1164 PageDescriptor
->Flags
.Type
= MM_PHYSICAL_PAGE_FREE
;
1165 if (NT_SUCCESS(Status
))
1167 InsertHeadList(&FreeZeroedPageListHead
, ListEntry
);
1172 InsertHeadList(&FreeUnzeroedPageListHead
, ListEntry
);
1173 UnzeroedPageCount
++;
1177 DPRINT("Zeroed %d pages.\n", Count
);
1178 KeResetEvent(&ZeroPageThreadEvent
);
1179 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
1182 return STATUS_SUCCESS
;