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 /* GLOBALS ****************************************************************/
28 // ReactOS to NT Physical Page Descriptor Entry Legacy Mapping Definitions
32 #define RmapListHead AweReferenceCount
33 #define PHYSICAL_PAGE MMPFN
34 #define PPHYSICAL_PAGE PMMPFN
36 PPHYSICAL_PAGE MmPfnDatabase
;
38 PFN_NUMBER MmAvailablePages
;
39 PFN_NUMBER MmResidentAvailablePages
;
40 PFN_NUMBER MmResidentAvailableAtInit
;
42 SIZE_T MmTotalCommitLimit
;
43 SIZE_T MmTotalCommittedPages
;
44 SIZE_T MmSharedCommit
;
45 SIZE_T MmDriverCommit
;
46 SIZE_T MmProcessCommit
;
47 SIZE_T MmPagedPoolCommit
;
48 SIZE_T MmPeakCommitment
;
49 SIZE_T MmtotalCommitLimitMaximum
;
51 KEVENT ZeroPageThreadEvent
;
52 static BOOLEAN ZeroPageThreadShouldTerminate
= FALSE
;
53 static RTL_BITMAP MiUserPfnBitMap
;
55 /* FUNCTIONS *************************************************************/
59 MiInitializeUserPfnBitmap(VOID
)
63 /* Allocate enough buffer for the PFN bitmap and align it on 32-bits */
64 Bitmap
= ExAllocatePoolWithTag(NonPagedPool
,
65 (((MmHighestPhysicalPage
+ 1) + 31) / 32) * 4,
69 /* Initialize it and clear all the bits to begin with */
70 RtlInitializeBitMap(&MiUserPfnBitMap
,
72 MmHighestPhysicalPage
+ 1);
73 RtlClearAllBits(&MiUserPfnBitMap
);
78 MmGetLRUFirstUserPage(VOID
)
83 /* Find the first user page */
84 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
85 Position
= RtlFindSetBits(&MiUserPfnBitMap
, 1, 0);
86 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
87 if (Position
== 0xFFFFFFFF) return 0;
95 MmInsertLRULastUserPage(PFN_NUMBER Pfn
)
99 /* Set the page as a user page */
100 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
101 RtlSetBit(&MiUserPfnBitMap
, Pfn
);
102 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
107 MmGetLRUNextUserPage(PFN_NUMBER PreviousPfn
)
112 /* Find the next user page */
113 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
114 Position
= RtlFindSetBits(&MiUserPfnBitMap
, 1, PreviousPfn
+ 1);
115 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
116 if (Position
== 0xFFFFFFFF) return 0;
124 MmRemoveLRUUserPage(PFN_NUMBER Page
)
126 /* Unset the page as a user page */
127 RtlClearBit(&MiUserPfnBitMap
, Page
);
132 MiIsPfnFree(IN PMMPFN Pfn1
)
134 /* Must be a free or zero page, with no references, linked */
135 return ((Pfn1
->u3
.e1
.PageLocation
<= StandbyPageList
) &&
138 !(Pfn1
->u3
.e2
.ReferenceCount
));
143 MiIsPfnInUse(IN PMMPFN Pfn1
)
145 /* Standby list or higher, unlinked, and with references */
146 return !MiIsPfnFree(Pfn1
);
151 MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress
,
152 IN PHYSICAL_ADDRESS HighAddress
,
153 IN PHYSICAL_ADDRESS SkipBytes
,
154 IN SIZE_T TotalBytes
,
155 IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute
,
159 PFN_NUMBER PageCount
, LowPage
, HighPage
, SkipPages
, PagesFound
= 0, Page
;
160 PPFN_NUMBER MdlPage
, LastMdlPage
;
163 INT LookForZeroedPages
;
164 ASSERT (KeGetCurrentIrql() <= APC_LEVEL
);
167 // Convert the low address into a PFN
169 LowPage
= (PFN_NUMBER
)(LowAddress
.QuadPart
>> PAGE_SHIFT
);
172 // Convert, and normalize, the high address into a PFN
174 HighPage
= (PFN_NUMBER
)(HighAddress
.QuadPart
>> PAGE_SHIFT
);
175 if (HighPage
> MmHighestPhysicalPage
) HighPage
= MmHighestPhysicalPage
;
178 // Validate skipbytes and convert them into pages
180 if (BYTE_OFFSET(SkipBytes
.LowPart
)) return NULL
;
181 SkipPages
= (PFN_NUMBER
)(SkipBytes
.QuadPart
>> PAGE_SHIFT
);
183 /* This isn't supported at all */
184 if (SkipPages
) DPRINT1("WARNING: Caller requesting SkipBytes, MDL might be mismatched\n");
187 // Now compute the number of pages the MDL will cover
189 PageCount
= (PFN_NUMBER
)ADDRESS_AND_SIZE_TO_SPAN_PAGES(0, TotalBytes
);
193 // Try creating an MDL for these many pages
195 Mdl
= MmCreateMdl(NULL
, NULL
, PageCount
<< PAGE_SHIFT
);
199 // This function is not required to return the amount of pages requested
200 // In fact, it can return as little as 1 page, and callers are supposed
201 // to deal with this scenario. So re-attempt the allocation with less
202 // pages than before, and see if it worked this time.
204 PageCount
-= (PageCount
>> 4);
208 // Wow, not even a single page was around!
210 if (!Mdl
) return NULL
;
213 // This is where the page array starts....
215 MdlPage
= (PPFN_NUMBER
)(Mdl
+ 1);
218 // Lock the PFN database
220 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
223 // Are we looking for any pages, without discriminating?
225 if ((LowPage
== 0) && (HighPage
== MmHighestPhysicalPage
))
228 // Well then, let's go shopping
230 while (PagesFound
< PageCount
)
233 Page
= MiRemoveAnyPage(0);
236 /* This is not good... hopefully we have at least SOME pages */
241 /* Grab the page entry for it */
242 Pfn1
= MiGetPfnEntry(Page
);
245 // Make sure it's really free
247 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
250 // Allocate it and mark it
252 Pfn1
->u3
.e1
.StartOfAllocation
= 1;
253 Pfn1
->u3
.e1
.EndOfAllocation
= 1;
254 Pfn1
->u3
.e2
.ReferenceCount
= 1;
257 // Save it into the MDL
259 *MdlPage
++ = MiGetPfnEntryIndex(Pfn1
);
266 // You want specific range of pages. We'll do this in two runs
268 for (LookForZeroedPages
= 1; LookForZeroedPages
>= 0; LookForZeroedPages
--)
271 // Scan the range you specified
273 for (Page
= LowPage
; Page
< HighPage
; Page
++)
276 // Get the PFN entry for this page
278 Pfn1
= MiGetPfnEntry(Page
);
282 // Make sure it's free and if this is our first pass, zeroed
284 if (MiIsPfnInUse(Pfn1
)) continue;
285 if ((Pfn1
->u3
.e1
.PageLocation
== ZeroedPageList
) != LookForZeroedPages
) continue;
287 /* Remove the page from the free or zero list */
288 MiUnlinkFreeOrZeroedPage(Pfn1
);
293 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
296 // Now setup the page and mark it
298 Pfn1
->u3
.e2
.ReferenceCount
= 1;
299 Pfn1
->u3
.e1
.StartOfAllocation
= 1;
300 Pfn1
->u3
.e1
.EndOfAllocation
= 1;
303 // Save this page into the MDL
306 if (++PagesFound
== PageCount
) break;
310 // If the first pass was enough, don't keep going, otherwise, go again
312 if (PagesFound
== PageCount
) break;
317 // Now release the PFN count
319 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
322 // We might've found less pages, but not more ;-)
324 if (PagesFound
!= PageCount
) ASSERT(PagesFound
< PageCount
);
328 // If we didn' tfind any pages at all, fail
330 DPRINT1("NO MDL PAGES!\n");
336 // Write out how many pages we found
338 Mdl
->ByteCount
= (ULONG
)(PagesFound
<< PAGE_SHIFT
);
341 // Terminate the MDL array if there's certain missing pages
343 if (PagesFound
!= PageCount
) *MdlPage
= -1;
346 // Now go back and loop over all the MDL pages
348 MdlPage
= (PPFN_NUMBER
)(Mdl
+ 1);
349 LastMdlPage
= MdlPage
+ PagesFound
;
350 while (MdlPage
< LastMdlPage
)
353 // Check if we've reached the end
356 if (Page
== (PFN_NUMBER
)-1) break;
359 // Get the PFN entry for the page and check if we should zero it out
361 Pfn1
= MiGetPfnEntry(Page
);
363 if (Pfn1
->u3
.e1
.PageLocation
!= ZeroedPageList
) MiZeroPhysicalPage(Page
);
364 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
368 // We're done, mark the pages as locked (should we lock them, though???)
371 Mdl
->MdlFlags
|= MDL_PAGES_LOCKED
;
377 MmDumpPfnDatabase(VOID
)
381 PCHAR State
= "????", Type
= "Unknown";
383 ULONG Totals
[5] = {0}, FreePages
= 0;
385 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
388 // Loop the PFN database
390 for (i
= 0; i
<= MmHighestPhysicalPage
; i
++)
392 Pfn1
= MiGetPfnEntry(i
);
398 if (MiIsPfnInUse(Pfn1
))
411 // Pretty-print the page
413 DbgPrint("0x%08p:\t%04s\t%20s\t(%02d) [%08p])\n",
417 Pfn1
->u3
.e2
.ReferenceCount
,
421 DbgPrint("Nonpaged Pool: %d pages\t[%d KB]\n", Totals
[MC_NPPOOL
], (Totals
[MC_NPPOOL
] << PAGE_SHIFT
) / 1024);
422 DbgPrint("Paged Pool: %d pages\t[%d KB]\n", Totals
[MC_PPOOL
], (Totals
[MC_PPOOL
] << PAGE_SHIFT
) / 1024);
423 DbgPrint("File System Cache: %d pages\t[%d KB]\n", Totals
[MC_CACHE
], (Totals
[MC_CACHE
] << PAGE_SHIFT
) / 1024);
424 DbgPrint("Process Working Set: %d pages\t[%d KB]\n", Totals
[MC_USER
], (Totals
[MC_USER
] << PAGE_SHIFT
) / 1024);
425 DbgPrint("System: %d pages\t[%d KB]\n", Totals
[MC_SYSTEM
], (Totals
[MC_SYSTEM
] << PAGE_SHIFT
) / 1024);
426 DbgPrint("Free: %d pages\t[%d KB]\n", FreePages
, (FreePages
<< PAGE_SHIFT
) / 1024);
428 KeLowerIrql(OldIrql
);
433 MmSetRmapListHeadPage(PFN_NUMBER Pfn
, struct _MM_RMAP_ENTRY
* ListHead
)
438 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
439 Pfn1
= MiGetPfnEntry(Pfn
);
442 /* Should not be trying to insert an RMAP for a non-active page */
443 ASSERT(MiIsPfnInUse(Pfn1
) == TRUE
);
445 /* Set the list head address */
446 Pfn1
->RmapListHead
= (LONG
)ListHead
;
448 /* Mark that the page has an actual RMAP, not a residual color link */
449 Pfn1
->u3
.e1
.ParityError
= TRUE
;
453 /* ReactOS semantics dictate the page is STILL active right now */
454 ASSERT(MiIsPfnInUse(Pfn1
) == TRUE
);
456 /* In this case, the RMAP is actually being removed, so clear field */
457 Pfn1
->RmapListHead
= 0;
459 /* Mark that the page has no RMAP, not a residual color link */
460 Pfn1
->u3
.e1
.ParityError
= FALSE
;
462 /* ReactOS semantics will now release the page, which will make it free and enter a colored list */
465 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
468 struct _MM_RMAP_ENTRY
*
470 MmGetRmapListHeadPage(PFN_NUMBER Pfn
)
473 struct _MM_RMAP_ENTRY
* ListHead
;
476 /* Lock PFN database */
477 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
480 Pfn1
= MiGetPfnEntry(Pfn
);
482 /* Check if the page doesn't really have an RMAP */
483 if (Pfn1
->u3
.e1
.ParityError
== FALSE
)
485 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
489 ListHead
= (struct _MM_RMAP_ENTRY
*)Pfn1
->RmapListHead
;
491 /* Should not have an RMAP for a non-active page */
492 ASSERT(MiIsPfnInUse(Pfn1
) == TRUE
);
494 /* Release PFN database and return rmap list head */
495 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
501 MmSetSavedSwapEntryPage(PFN_NUMBER Pfn
, SWAPENTRY SwapEntry
)
505 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
506 MiGetPfnEntry(Pfn
)->u1
.WsIndex
= SwapEntry
;
507 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
512 MmGetSavedSwapEntryPage(PFN_NUMBER Pfn
)
517 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
518 SwapEntry
= MiGetPfnEntry(Pfn
)->u1
.WsIndex
;
519 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
526 MmReferencePage(PFN_NUMBER Pfn
)
530 DPRINT("MmReferencePage(PysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
532 if (Pfn
== 0 || Pfn
> MmHighestPhysicalPage
)
537 Page
= MiGetPfnEntry(Pfn
);
540 Page
->u3
.e2
.ReferenceCount
++;
545 MmGetReferenceCountPage(PFN_NUMBER Pfn
)
551 DPRINT("MmGetReferenceCountPage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
553 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
554 Page
= MiGetPfnEntry(Pfn
);
557 RCount
= Page
->u3
.e2
.ReferenceCount
;
559 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
565 MmIsPageInUse(PFN_NUMBER Pfn
)
567 return MiIsPfnInUse(MiGetPfnEntry(Pfn
));
572 MiSetConsumer(IN PFN_NUMBER Pfn
,
575 MiGetPfnEntry(Pfn
)->u3
.e1
.PageLocation
= ActiveAndValid
;
580 MmDereferencePage(PFN_NUMBER Pfn
)
584 DPRINT("MmDereferencePage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
586 Page
= MiGetPfnEntry(Pfn
);
589 Page
->u3
.e2
.ReferenceCount
--;
590 if (Page
->u3
.e2
.ReferenceCount
== 0)
592 /* Mark the page temporarily as valid, we're going to make it free soon */
593 Page
->u3
.e1
.PageLocation
= ActiveAndValid
;
595 /* Bring it back into the free list */
596 MiInsertPageInFreeList(Pfn
);
602 MmAllocPage(ULONG Type
)
604 PFN_NUMBER PfnOffset
;
607 if (Type
!= MC_SYSTEM
)
609 PfnOffset
= MiRemoveZeroPage(0);
613 PfnOffset
= MiRemoveAnyPage(0);
618 DPRINT1("MmAllocPage(): Out of memory\n");
622 Pfn1
= MiGetPfnEntry(PfnOffset
);
623 Pfn1
->u3
.e2
.ReferenceCount
= 1;
624 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
630 MmZeroPageThreadMain(PVOID Ignored
)
635 PFN_NUMBER PageIndex
, FreePage
;
639 /* Free initial kernel memory */
640 //MiFreeInitMemory();
642 /* Set our priority to 0 */
643 KeGetCurrentThread()->BasePriority
= 0;
644 KeSetPriorityThread(KeGetCurrentThread(), 0);
648 Status
= KeWaitForSingleObject(&ZeroPageThreadEvent
,
654 if (ZeroPageThreadShouldTerminate
)
656 DPRINT1("ZeroPageThread: Terminating\n");
657 return STATUS_SUCCESS
;
660 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
661 while (MmFreePageListHead
.Total
)
663 PageIndex
= MmFreePageListHead
.Flink
;
664 Pfn1
= MiGetPfnEntry(PageIndex
);
665 FreePage
= MiRemoveAnyPage(0); // FIXME: Use real color
666 if (FreePage
!= PageIndex
)
668 KeBugCheckEx(PFN_LIST_CORRUPT
,
675 Pfn1
->u1
.Flink
= LIST_HEAD
;
676 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
678 ZeroAddress
= MiMapPagesToZeroInHyperSpace(Pfn1
, 1);
680 RtlZeroMemory(ZeroAddress
, PAGE_SIZE
);
681 MiUnmapPagesInZeroSpace(ZeroAddress
, 1);
683 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
685 MiInsertPageInList(&MmZeroedPageListHead
, PageIndex
);
688 DPRINT("Zeroed %d pages.\n", Count
);
689 KeResetEvent(&ZeroPageThreadEvent
);
690 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
693 return STATUS_SUCCESS
;