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 ****************************************************************/
26 // ReactOS to NT Physical Page Descriptor Entry Legacy Mapping Definitions
27 #define PHYSICAL_PAGE MMPFN
28 #define PPHYSICAL_PAGE PMMPFN
30 PPHYSICAL_PAGE MmPfnDatabase
;
32 PFN_NUMBER MmAvailablePages
;
33 PFN_NUMBER MmResidentAvailablePages
;
34 PFN_NUMBER MmResidentAvailableAtInit
;
36 SIZE_T MmTotalCommitLimit
;
37 SIZE_T MmTotalCommittedPages
;
38 SIZE_T MmSharedCommit
;
39 SIZE_T MmDriverCommit
;
40 SIZE_T MmProcessCommit
;
41 SIZE_T MmPagedPoolCommit
;
42 SIZE_T MmPeakCommitment
;
43 SIZE_T MmtotalCommitLimitMaximum
;
45 static RTL_BITMAP MiUserPfnBitMap
;
47 /* FUNCTIONS *************************************************************/
51 MiInitializeUserPfnBitmap(VOID
)
55 /* Allocate enough buffer for the PFN bitmap and align it on 32-bits */
56 Bitmap
= ExAllocatePoolWithTag(NonPagedPool
,
57 (((MmHighestPhysicalPage
+ 1) + 31) / 32) * 4,
61 /* Initialize it and clear all the bits to begin with */
62 RtlInitializeBitMap(&MiUserPfnBitMap
,
64 MmHighestPhysicalPage
+ 1);
65 RtlClearAllBits(&MiUserPfnBitMap
);
70 MmGetLRUFirstUserPage(VOID
)
75 /* Find the first user page */
76 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
77 Position
= RtlFindSetBits(&MiUserPfnBitMap
, 1, 0);
78 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
79 if (Position
== 0xFFFFFFFF) return 0;
87 MmInsertLRULastUserPage(PFN_NUMBER Pfn
)
91 /* Set the page as a user page */
92 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
93 RtlSetBit(&MiUserPfnBitMap
, Pfn
);
94 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
99 MmGetLRUNextUserPage(PFN_NUMBER PreviousPfn
)
104 /* Find the next user page */
105 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
106 Position
= RtlFindSetBits(&MiUserPfnBitMap
, 1, PreviousPfn
+ 1);
107 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
108 if (Position
== 0xFFFFFFFF) return 0;
116 MmRemoveLRUUserPage(PFN_NUMBER Page
)
118 /* Unset the page as a user page */
119 RtlClearBit(&MiUserPfnBitMap
, Page
);
124 MiIsPfnFree(IN PMMPFN Pfn1
)
126 /* Must be a free or zero page, with no references, linked */
127 return ((Pfn1
->u3
.e1
.PageLocation
<= StandbyPageList
) &&
130 !(Pfn1
->u3
.e2
.ReferenceCount
));
135 MiIsPfnInUse(IN PMMPFN Pfn1
)
137 /* Standby list or higher, unlinked, and with references */
138 return !MiIsPfnFree(Pfn1
);
143 MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress
,
144 IN PHYSICAL_ADDRESS HighAddress
,
145 IN PHYSICAL_ADDRESS SkipBytes
,
146 IN SIZE_T TotalBytes
,
147 IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute
,
151 PFN_NUMBER PageCount
, LowPage
, HighPage
, SkipPages
, PagesFound
= 0, Page
;
152 PPFN_NUMBER MdlPage
, LastMdlPage
;
155 INT LookForZeroedPages
;
156 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
159 // Convert the low address into a PFN
161 LowPage
= (PFN_NUMBER
)(LowAddress
.QuadPart
>> PAGE_SHIFT
);
164 // Convert, and normalize, the high address into a PFN
166 HighPage
= (PFN_NUMBER
)(HighAddress
.QuadPart
>> PAGE_SHIFT
);
167 if (HighPage
> MmHighestPhysicalPage
) HighPage
= MmHighestPhysicalPage
;
170 // Validate skipbytes and convert them into pages
172 if (BYTE_OFFSET(SkipBytes
.LowPart
)) return NULL
;
173 SkipPages
= (PFN_NUMBER
)(SkipBytes
.QuadPart
>> PAGE_SHIFT
);
175 /* This isn't supported at all */
176 if (SkipPages
) DPRINT1("WARNING: Caller requesting SkipBytes, MDL might be mismatched\n");
179 // Now compute the number of pages the MDL will cover
181 PageCount
= (PFN_NUMBER
)ADDRESS_AND_SIZE_TO_SPAN_PAGES(0, TotalBytes
);
185 // Try creating an MDL for these many pages
187 Mdl
= MmCreateMdl(NULL
, NULL
, PageCount
<< PAGE_SHIFT
);
191 // This function is not required to return the amount of pages requested
192 // In fact, it can return as little as 1 page, and callers are supposed
193 // to deal with this scenario. So re-attempt the allocation with less
194 // pages than before, and see if it worked this time.
196 PageCount
-= (PageCount
>> 4);
200 // Wow, not even a single page was around!
202 if (!Mdl
) return NULL
;
205 // This is where the page array starts....
207 MdlPage
= (PPFN_NUMBER
)(Mdl
+ 1);
210 // Lock the PFN database
212 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
215 // Are we looking for any pages, without discriminating?
217 if ((LowPage
== 0) && (HighPage
== MmHighestPhysicalPage
))
220 // Well then, let's go shopping
222 while (PagesFound
< PageCount
)
225 Page
= MiRemoveAnyPage(0);
228 /* This is not good... hopefully we have at least SOME pages */
233 /* Grab the page entry for it */
234 Pfn1
= MiGetPfnEntry(Page
);
237 // Make sure it's really free
239 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
242 // Allocate it and mark it
244 Pfn1
->u3
.e1
.StartOfAllocation
= 1;
245 Pfn1
->u3
.e1
.EndOfAllocation
= 1;
246 Pfn1
->u3
.e2
.ReferenceCount
= 1;
249 // Save it into the MDL
251 *MdlPage
++ = MiGetPfnEntryIndex(Pfn1
);
258 // You want specific range of pages. We'll do this in two runs
260 for (LookForZeroedPages
= 1; LookForZeroedPages
>= 0; LookForZeroedPages
--)
263 // Scan the range you specified
265 for (Page
= LowPage
; Page
< HighPage
; Page
++)
268 // Get the PFN entry for this page
270 Pfn1
= MiGetPfnEntry(Page
);
274 // Make sure it's free and if this is our first pass, zeroed
276 if (MiIsPfnInUse(Pfn1
)) continue;
277 if ((Pfn1
->u3
.e1
.PageLocation
== ZeroedPageList
) != LookForZeroedPages
) continue;
279 /* Remove the page from the free or zero list */
280 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
281 MiUnlinkFreeOrZeroedPage(Pfn1
);
286 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
289 // Now setup the page and mark it
291 Pfn1
->u3
.e2
.ReferenceCount
= 1;
292 Pfn1
->u2
.ShareCount
= 1;
293 MI_SET_PFN_DELETED(Pfn1
);
294 Pfn1
->u4
.PteFrame
= 0x1FFEDCB;
295 Pfn1
->u3
.e1
.StartOfAllocation
= 1;
296 Pfn1
->u3
.e1
.EndOfAllocation
= 1;
297 Pfn1
->u4
.VerifierAllocation
= 0;
300 // Save this page into the MDL
303 if (++PagesFound
== PageCount
) break;
307 // If the first pass was enough, don't keep going, otherwise, go again
309 if (PagesFound
== PageCount
) break;
314 // Now release the PFN count
316 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
319 // We might've found less pages, but not more ;-)
321 if (PagesFound
!= PageCount
) ASSERT(PagesFound
< PageCount
);
325 // If we didn' tfind any pages at all, fail
327 DPRINT1("NO MDL PAGES!\n");
333 // Write out how many pages we found
335 Mdl
->ByteCount
= (ULONG
)(PagesFound
<< PAGE_SHIFT
);
338 // Terminate the MDL array if there's certain missing pages
340 if (PagesFound
!= PageCount
) *MdlPage
= LIST_HEAD
;
343 // Now go back and loop over all the MDL pages
345 MdlPage
= (PPFN_NUMBER
)(Mdl
+ 1);
346 LastMdlPage
= MdlPage
+ PagesFound
;
347 while (MdlPage
< LastMdlPage
)
350 // Check if we've reached the end
353 if (Page
== LIST_HEAD
) break;
356 // Get the PFN entry for the page and check if we should zero it out
358 Pfn1
= MiGetPfnEntry(Page
);
360 if (Pfn1
->u3
.e1
.PageLocation
!= ZeroedPageList
) MiZeroPhysicalPage(Page
);
361 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
365 // We're done, mark the pages as locked (should we lock them, though???)
368 Mdl
->MdlFlags
|= MDL_PAGES_LOCKED
;
374 MmDumpPfnDatabase(VOID
)
376 /* Pretty useless for now, to be improved later */
382 MmSetRmapListHeadPage(PFN_NUMBER Pfn
, PMM_RMAP_ENTRY ListHead
)
387 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
388 Pfn1
= MiGetPfnEntry(Pfn
);
390 ASSERT_IS_ROS_PFN(Pfn1
);
394 /* Should not be trying to insert an RMAP for a non-active page */
395 ASSERT(MiIsPfnInUse(Pfn1
) == TRUE
);
397 /* Set the list head address */
398 MI_GET_ROS_DATA(Pfn1
)->RmapListHead
= ListHead
;
402 /* ReactOS semantics dictate the page is STILL active right now */
403 ASSERT(MiIsPfnInUse(Pfn1
) == TRUE
);
405 /* In this case, the RMAP is actually being removed, so clear field */
406 MI_GET_ROS_DATA(Pfn1
)->RmapListHead
= NULL
;
408 /* ReactOS semantics will now release the page, which will make it free and enter a colored list */
411 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
416 MmGetRmapListHeadPage(PFN_NUMBER Pfn
)
419 PMM_RMAP_ENTRY ListHead
;
422 /* Lock PFN database */
423 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
426 Pfn1
= MiGetPfnEntry(Pfn
);
428 ASSERT_IS_ROS_PFN(Pfn1
);
430 /* Get the list head */
431 ListHead
= MI_GET_ROS_DATA(Pfn1
)->RmapListHead
;
433 /* Should not have an RMAP for a non-active page */
434 ASSERT(MiIsPfnInUse(Pfn1
) == TRUE
);
436 /* Release PFN database and return rmap list head */
437 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
443 MmSetSavedSwapEntryPage(PFN_NUMBER Pfn
, SWAPENTRY SwapEntry
)
448 Page
= MiGetPfnEntry(Pfn
);
450 ASSERT_IS_ROS_PFN(Page
);
452 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
453 MI_GET_ROS_DATA(Page
)->SwapEntry
= SwapEntry
;
454 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
459 MmGetSavedSwapEntryPage(PFN_NUMBER Pfn
)
465 Page
= MiGetPfnEntry(Pfn
);
467 ASSERT_IS_ROS_PFN(Page
);
469 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
470 SwapEntry
= MI_GET_ROS_DATA(Page
)->SwapEntry
;
471 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
478 MmReferencePage(PFN_NUMBER Pfn
)
482 DPRINT("MmReferencePage(PysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
484 if (Pfn
== 0 || Pfn
> MmHighestPhysicalPage
)
489 Page
= MiGetPfnEntry(Pfn
);
491 ASSERT_IS_ROS_PFN(Page
);
493 Page
->u3
.e2
.ReferenceCount
++;
498 MmGetReferenceCountPage(PFN_NUMBER Pfn
)
504 DPRINT("MmGetReferenceCountPage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
506 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
507 Page
= MiGetPfnEntry(Pfn
);
509 ASSERT_IS_ROS_PFN(Page
);
511 RCount
= Page
->u3
.e2
.ReferenceCount
;
513 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
519 MmIsPageInUse(PFN_NUMBER Pfn
)
521 return MiIsPfnInUse(MiGetPfnEntry(Pfn
));
526 MmDereferencePage(PFN_NUMBER Pfn
)
529 DPRINT("MmDereferencePage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
531 Page
= MiGetPfnEntry(Pfn
);
533 ASSERT_IS_ROS_PFN(Page
);
535 Page
->u3
.e2
.ReferenceCount
--;
536 if (Page
->u3
.e2
.ReferenceCount
== 0)
538 /* Mark the page temporarily as valid, we're going to make it free soon */
539 Page
->u3
.e1
.PageLocation
= ActiveAndValid
;
541 /* It's not a ROS PFN anymore */
542 Page
->u4
.AweAllocation
= FALSE
;
543 ExFreePool(MI_GET_ROS_DATA(Page
));
546 /* Bring it back into the free list */
547 DPRINT("Legacy free: %lx\n", Pfn
);
548 MiInsertPageInFreeList(Pfn
);
554 MmAllocPage(ULONG Type
)
556 PFN_NUMBER PfnOffset
;
559 PfnOffset
= MiRemoveZeroPage(MI_GET_NEXT_COLOR());
563 DPRINT1("MmAllocPage(): Out of memory\n");
567 DPRINT("Legacy allocate: %lx\n", PfnOffset
);
568 Pfn1
= MiGetPfnEntry(PfnOffset
);
569 Pfn1
->u3
.e2
.ReferenceCount
= 1;
570 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
572 /* This marks the PFN as a ReactOS PFN */
573 Pfn1
->u4
.AweAllocation
= TRUE
;
575 /* Allocate the extra ReactOS Data and zero it out */
576 Pfn1
->RosMmData
= (LONG
)ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMROSPFN
), 'RsPf');
577 ASSERT(MI_GET_ROS_DATA(Pfn1
) != NULL
);
578 ASSERT_IS_ROS_PFN(Pfn1
);
579 MI_GET_ROS_DATA(Pfn1
)->SwapEntry
= 0;
580 MI_GET_ROS_DATA(Pfn1
)->RmapListHead
= NULL
;