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 static RTL_BITMAP MiUserPfnBitMap
;
53 /* FUNCTIONS *************************************************************/
57 MiInitializeUserPfnBitmap(VOID
)
61 /* Allocate enough buffer for the PFN bitmap and align it on 32-bits */
62 Bitmap
= ExAllocatePoolWithTag(NonPagedPool
,
63 (((MmHighestPhysicalPage
+ 1) + 31) / 32) * 4,
67 /* Initialize it and clear all the bits to begin with */
68 RtlInitializeBitMap(&MiUserPfnBitMap
,
70 MmHighestPhysicalPage
+ 1);
71 RtlClearAllBits(&MiUserPfnBitMap
);
76 MmGetLRUFirstUserPage(VOID
)
81 /* Find the first user page */
82 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
83 Position
= RtlFindSetBits(&MiUserPfnBitMap
, 1, 0);
84 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
85 if (Position
== 0xFFFFFFFF) return 0;
93 MmInsertLRULastUserPage(PFN_NUMBER Pfn
)
97 /* Set the page as a user page */
98 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
99 RtlSetBit(&MiUserPfnBitMap
, Pfn
);
100 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
105 MmGetLRUNextUserPage(PFN_NUMBER PreviousPfn
)
110 /* Find the next user page */
111 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
112 Position
= RtlFindSetBits(&MiUserPfnBitMap
, 1, PreviousPfn
+ 1);
113 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
114 if (Position
== 0xFFFFFFFF) return 0;
122 MmRemoveLRUUserPage(PFN_NUMBER Page
)
124 /* Unset the page as a user page */
125 RtlClearBit(&MiUserPfnBitMap
, Page
);
130 MiIsPfnFree(IN PMMPFN Pfn1
)
132 /* Must be a free or zero page, with no references, linked */
133 return ((Pfn1
->u3
.e1
.PageLocation
<= StandbyPageList
) &&
136 !(Pfn1
->u3
.e2
.ReferenceCount
));
141 MiIsPfnInUse(IN PMMPFN Pfn1
)
143 /* Standby list or higher, unlinked, and with references */
144 return !MiIsPfnFree(Pfn1
);
149 MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress
,
150 IN PHYSICAL_ADDRESS HighAddress
,
151 IN PHYSICAL_ADDRESS SkipBytes
,
152 IN SIZE_T TotalBytes
,
153 IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute
,
157 PFN_NUMBER PageCount
, LowPage
, HighPage
, SkipPages
, PagesFound
= 0, Page
;
158 PPFN_NUMBER MdlPage
, LastMdlPage
;
161 INT LookForZeroedPages
;
162 ASSERT (KeGetCurrentIrql() <= APC_LEVEL
);
165 // Convert the low address into a PFN
167 LowPage
= (PFN_NUMBER
)(LowAddress
.QuadPart
>> PAGE_SHIFT
);
170 // Convert, and normalize, the high address into a PFN
172 HighPage
= (PFN_NUMBER
)(HighAddress
.QuadPart
>> PAGE_SHIFT
);
173 if (HighPage
> MmHighestPhysicalPage
) HighPage
= MmHighestPhysicalPage
;
176 // Validate skipbytes and convert them into pages
178 if (BYTE_OFFSET(SkipBytes
.LowPart
)) return NULL
;
179 SkipPages
= (PFN_NUMBER
)(SkipBytes
.QuadPart
>> PAGE_SHIFT
);
181 /* This isn't supported at all */
182 if (SkipPages
) DPRINT1("WARNING: Caller requesting SkipBytes, MDL might be mismatched\n");
185 // Now compute the number of pages the MDL will cover
187 PageCount
= (PFN_NUMBER
)ADDRESS_AND_SIZE_TO_SPAN_PAGES(0, TotalBytes
);
191 // Try creating an MDL for these many pages
193 Mdl
= MmCreateMdl(NULL
, NULL
, PageCount
<< PAGE_SHIFT
);
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.
202 PageCount
-= (PageCount
>> 4);
206 // Wow, not even a single page was around!
208 if (!Mdl
) return NULL
;
211 // This is where the page array starts....
213 MdlPage
= (PPFN_NUMBER
)(Mdl
+ 1);
216 // Lock the PFN database
218 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
221 // Are we looking for any pages, without discriminating?
223 if ((LowPage
== 0) && (HighPage
== MmHighestPhysicalPage
))
226 // Well then, let's go shopping
228 while (PagesFound
< PageCount
)
231 Page
= MiRemoveAnyPage(0);
234 /* This is not good... hopefully we have at least SOME pages */
239 /* Grab the page entry for it */
240 Pfn1
= MiGetPfnEntry(Page
);
243 // Make sure it's really free
245 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
248 // Allocate it and mark it
250 Pfn1
->u3
.e1
.StartOfAllocation
= 1;
251 Pfn1
->u3
.e1
.EndOfAllocation
= 1;
252 Pfn1
->u3
.e2
.ReferenceCount
= 1;
255 // Save it into the MDL
257 *MdlPage
++ = MiGetPfnEntryIndex(Pfn1
);
264 // You want specific range of pages. We'll do this in two runs
266 for (LookForZeroedPages
= 1; LookForZeroedPages
>= 0; LookForZeroedPages
--)
269 // Scan the range you specified
271 for (Page
= LowPage
; Page
< HighPage
; Page
++)
274 // Get the PFN entry for this page
276 Pfn1
= MiGetPfnEntry(Page
);
280 // Make sure it's free and if this is our first pass, zeroed
282 if (MiIsPfnInUse(Pfn1
)) continue;
283 if ((Pfn1
->u3
.e1
.PageLocation
== ZeroedPageList
) != LookForZeroedPages
) continue;
285 /* Remove the page from the free or zero list */
286 MiUnlinkFreeOrZeroedPage(Pfn1
);
291 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
294 // Now setup the page and mark it
296 Pfn1
->u3
.e2
.ReferenceCount
= 1;
297 Pfn1
->u3
.e1
.StartOfAllocation
= 1;
298 Pfn1
->u3
.e1
.EndOfAllocation
= 1;
301 // Save this page into the MDL
304 if (++PagesFound
== PageCount
) break;
308 // If the first pass was enough, don't keep going, otherwise, go again
310 if (PagesFound
== PageCount
) break;
315 // Now release the PFN count
317 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
320 // We might've found less pages, but not more ;-)
322 if (PagesFound
!= PageCount
) ASSERT(PagesFound
< PageCount
);
326 // If we didn' tfind any pages at all, fail
328 DPRINT1("NO MDL PAGES!\n");
334 // Write out how many pages we found
336 Mdl
->ByteCount
= (ULONG
)(PagesFound
<< PAGE_SHIFT
);
339 // Terminate the MDL array if there's certain missing pages
341 if (PagesFound
!= PageCount
) *MdlPage
= -1;
344 // Now go back and loop over all the MDL pages
346 MdlPage
= (PPFN_NUMBER
)(Mdl
+ 1);
347 LastMdlPage
= MdlPage
+ PagesFound
;
348 while (MdlPage
< LastMdlPage
)
351 // Check if we've reached the end
354 if (Page
== (PFN_NUMBER
)-1) break;
357 // Get the PFN entry for the page and check if we should zero it out
359 Pfn1
= MiGetPfnEntry(Page
);
361 if (Pfn1
->u3
.e1
.PageLocation
!= ZeroedPageList
) MiZeroPhysicalPage(Page
);
362 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
366 // We're done, mark the pages as locked (should we lock them, though???)
369 Mdl
->MdlFlags
|= MDL_PAGES_LOCKED
;
375 MmDumpPfnDatabase(VOID
)
379 PCHAR State
= "????", Type
= "Unknown";
381 ULONG Totals
[5] = {0}, FreePages
= 0;
383 KeRaiseIrql(HIGH_LEVEL
, &OldIrql
);
386 // Loop the PFN database
388 for (i
= 0; i
<= MmHighestPhysicalPage
; i
++)
390 Pfn1
= MiGetPfnEntry(i
);
396 if (MiIsPfnInUse(Pfn1
))
409 // Pretty-print the page
411 DbgPrint("0x%08p:\t%04s\t%20s\t(%02d) [%08p])\n",
415 Pfn1
->u3
.e2
.ReferenceCount
,
419 DbgPrint("Nonpaged Pool: %d pages\t[%d KB]\n", Totals
[MC_NPPOOL
], (Totals
[MC_NPPOOL
] << PAGE_SHIFT
) / 1024);
420 DbgPrint("Paged Pool: %d pages\t[%d KB]\n", Totals
[MC_PPOOL
], (Totals
[MC_PPOOL
] << PAGE_SHIFT
) / 1024);
421 DbgPrint("File System Cache: %d pages\t[%d KB]\n", Totals
[MC_CACHE
], (Totals
[MC_CACHE
] << PAGE_SHIFT
) / 1024);
422 DbgPrint("Process Working Set: %d pages\t[%d KB]\n", Totals
[MC_USER
], (Totals
[MC_USER
] << PAGE_SHIFT
) / 1024);
423 DbgPrint("System: %d pages\t[%d KB]\n", Totals
[MC_SYSTEM
], (Totals
[MC_SYSTEM
] << PAGE_SHIFT
) / 1024);
424 DbgPrint("Free: %d pages\t[%d KB]\n", FreePages
, (FreePages
<< PAGE_SHIFT
) / 1024);
426 KeLowerIrql(OldIrql
);
431 MmSetRmapListHeadPage(PFN_NUMBER Pfn
, struct _MM_RMAP_ENTRY
* ListHead
)
436 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
437 Pfn1
= MiGetPfnEntry(Pfn
);
440 /* Should not be trying to insert an RMAP for a non-active page */
441 ASSERT(MiIsPfnInUse(Pfn1
) == TRUE
);
443 /* Set the list head address */
444 Pfn1
->RmapListHead
= (LONG
)ListHead
;
446 /* Mark that the page has an actual RMAP, not a residual color link */
447 Pfn1
->u3
.e1
.ParityError
= TRUE
;
451 /* ReactOS semantics dictate the page is STILL active right now */
452 ASSERT(MiIsPfnInUse(Pfn1
) == TRUE
);
454 /* In this case, the RMAP is actually being removed, so clear field */
455 Pfn1
->RmapListHead
= 0;
457 /* Mark that the page has no RMAP, not a residual color link */
458 Pfn1
->u3
.e1
.ParityError
= FALSE
;
460 /* ReactOS semantics will now release the page, which will make it free and enter a colored list */
463 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
466 struct _MM_RMAP_ENTRY
*
468 MmGetRmapListHeadPage(PFN_NUMBER Pfn
)
471 struct _MM_RMAP_ENTRY
* ListHead
;
474 /* Lock PFN database */
475 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
478 Pfn1
= MiGetPfnEntry(Pfn
);
480 /* Check if the page doesn't really have an RMAP */
481 if (Pfn1
->u3
.e1
.ParityError
== FALSE
)
483 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
487 ListHead
= (struct _MM_RMAP_ENTRY
*)Pfn1
->RmapListHead
;
489 /* Should not have an RMAP for a non-active page */
490 ASSERT(MiIsPfnInUse(Pfn1
) == TRUE
);
492 /* Release PFN database and return rmap list head */
493 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
499 MmSetSavedSwapEntryPage(PFN_NUMBER Pfn
, SWAPENTRY SwapEntry
)
503 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
504 MiGetPfnEntry(Pfn
)->u1
.WsIndex
= SwapEntry
;
505 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
510 MmGetSavedSwapEntryPage(PFN_NUMBER Pfn
)
515 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
516 SwapEntry
= MiGetPfnEntry(Pfn
)->u1
.WsIndex
;
517 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
524 MmReferencePage(PFN_NUMBER Pfn
)
528 DPRINT("MmReferencePage(PysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
530 if (Pfn
== 0 || Pfn
> MmHighestPhysicalPage
)
535 Page
= MiGetPfnEntry(Pfn
);
538 Page
->u3
.e2
.ReferenceCount
++;
543 MmGetReferenceCountPage(PFN_NUMBER Pfn
)
549 DPRINT("MmGetReferenceCountPage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
551 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
552 Page
= MiGetPfnEntry(Pfn
);
555 RCount
= Page
->u3
.e2
.ReferenceCount
;
557 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
563 MmIsPageInUse(PFN_NUMBER Pfn
)
565 return MiIsPfnInUse(MiGetPfnEntry(Pfn
));
570 MmDereferencePage(PFN_NUMBER Pfn
)
574 DPRINT("MmDereferencePage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
576 Page
= MiGetPfnEntry(Pfn
);
579 Page
->u3
.e2
.ReferenceCount
--;
580 if (Page
->u3
.e2
.ReferenceCount
== 0)
582 /* Mark the page temporarily as valid, we're going to make it free soon */
583 Page
->u3
.e1
.PageLocation
= ActiveAndValid
;
585 /* Bring it back into the free list */
586 DPRINT("Legacy free: %lx\n", Pfn
);
587 MiInsertPageInFreeList(Pfn
);
593 MmAllocPage(ULONG Type
)
595 PFN_NUMBER PfnOffset
;
598 if (Type
!= MC_SYSTEM
)
600 PfnOffset
= MiRemoveZeroPage(MI_GET_NEXT_COLOR());
604 PfnOffset
= MiRemoveAnyPage(MI_GET_NEXT_COLOR());
609 DPRINT1("MmAllocPage(): Out of memory\n");
613 DPRINT("Legacy allocate: %lx\n", PfnOffset
);
614 Pfn1
= MiGetPfnEntry(PfnOffset
);
615 Pfn1
->u3
.e2
.ReferenceCount
= 1;
616 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;