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 MmTotalCommittedPages
;
37 SIZE_T MmSharedCommit
;
38 SIZE_T MmDriverCommit
;
39 SIZE_T MmProcessCommit
;
40 SIZE_T MmPagedPoolCommit
;
41 SIZE_T MmPeakCommitment
;
42 SIZE_T MmtotalCommitLimitMaximum
;
44 static RTL_BITMAP MiUserPfnBitMap
;
46 /* FUNCTIONS *************************************************************/
50 MiInitializeUserPfnBitmap(VOID
)
54 /* Allocate enough buffer for the PFN bitmap and align it on 32-bits */
55 Bitmap
= ExAllocatePoolWithTag(NonPagedPool
,
56 (((MmHighestPhysicalPage
+ 1) + 31) / 32) * 4,
60 /* Initialize it and clear all the bits to begin with */
61 RtlInitializeBitMap(&MiUserPfnBitMap
,
63 (ULONG
)MmHighestPhysicalPage
+ 1);
64 RtlClearAllBits(&MiUserPfnBitMap
);
69 MmGetLRUFirstUserPage(VOID
)
74 /* Find the first user page */
75 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
76 Position
= RtlFindSetBits(&MiUserPfnBitMap
, 1, 0);
77 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
78 if (Position
== 0xFFFFFFFF) return 0;
81 ASSERT(Position
!= 0);
82 ASSERT_IS_ROS_PFN(MiGetPfnEntry(Position
));
88 MmInsertLRULastUserPage(PFN_NUMBER Pfn
)
92 /* Set the page as a user page */
94 ASSERT_IS_ROS_PFN(MiGetPfnEntry(Pfn
));
95 ASSERT(!RtlCheckBit(&MiUserPfnBitMap
, (ULONG
)Pfn
));
96 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
97 RtlSetBit(&MiUserPfnBitMap
, (ULONG
)Pfn
);
98 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
103 MmGetLRUNextUserPage(PFN_NUMBER PreviousPfn
)
108 /* Find the next user page */
109 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
110 Position
= RtlFindSetBits(&MiUserPfnBitMap
, 1, (ULONG
)PreviousPfn
+ 1);
111 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
112 if (Position
== 0xFFFFFFFF) return 0;
115 ASSERT(Position
!= 0);
116 ASSERT_IS_ROS_PFN(MiGetPfnEntry(Position
));
122 MmRemoveLRUUserPage(PFN_NUMBER Page
)
126 /* Unset the page as a user page */
128 ASSERT_IS_ROS_PFN(MiGetPfnEntry(Page
));
129 ASSERT(RtlCheckBit(&MiUserPfnBitMap
, (ULONG
)Page
));
130 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
131 RtlClearBit(&MiUserPfnBitMap
, (ULONG
)Page
);
132 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
137 MiIsPfnFree(IN PMMPFN Pfn1
)
139 /* Must be a free or zero page, with no references, linked */
140 return ((Pfn1
->u3
.e1
.PageLocation
<= StandbyPageList
) &&
143 !(Pfn1
->u3
.e2
.ReferenceCount
));
148 MiIsPfnInUse(IN PMMPFN Pfn1
)
150 /* Standby list or higher, unlinked, and with references */
151 return !MiIsPfnFree(Pfn1
);
156 MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress
,
157 IN PHYSICAL_ADDRESS HighAddress
,
158 IN PHYSICAL_ADDRESS SkipBytes
,
159 IN SIZE_T TotalBytes
,
160 IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute
,
164 PFN_NUMBER PageCount
, LowPage
, HighPage
, SkipPages
, PagesFound
= 0, Page
;
165 PPFN_NUMBER MdlPage
, LastMdlPage
;
168 INT LookForZeroedPages
;
169 ASSERT(KeGetCurrentIrql() <= APC_LEVEL
);
170 DPRINT1("ARM3-DEBUG: Being called with %I64x %I64x %I64x %lx %d %d\n", LowAddress
, HighAddress
, SkipBytes
, TotalBytes
, CacheAttribute
, MdlFlags
);
173 // Convert the low address into a PFN
175 LowPage
= (PFN_NUMBER
)(LowAddress
.QuadPart
>> PAGE_SHIFT
);
178 // Convert, and normalize, the high address into a PFN
180 HighPage
= (PFN_NUMBER
)(HighAddress
.QuadPart
>> PAGE_SHIFT
);
181 if (HighPage
> MmHighestPhysicalPage
) HighPage
= MmHighestPhysicalPage
;
184 // Validate skipbytes and convert them into pages
186 if (BYTE_OFFSET(SkipBytes
.LowPart
)) return NULL
;
187 SkipPages
= (PFN_NUMBER
)(SkipBytes
.QuadPart
>> PAGE_SHIFT
);
189 /* This isn't supported at all */
190 if (SkipPages
) DPRINT1("WARNING: Caller requesting SkipBytes, MDL might be mismatched\n");
193 // Now compute the number of pages the MDL will cover
195 PageCount
= (PFN_NUMBER
)ADDRESS_AND_SIZE_TO_SPAN_PAGES(0, TotalBytes
);
199 // Try creating an MDL for these many pages
201 Mdl
= MmCreateMdl(NULL
, NULL
, PageCount
<< PAGE_SHIFT
);
205 // This function is not required to return the amount of pages requested
206 // In fact, it can return as little as 1 page, and callers are supposed
207 // to deal with this scenario. So re-attempt the allocation with less
208 // pages than before, and see if it worked this time.
210 PageCount
-= (PageCount
>> 4);
214 // Wow, not even a single page was around!
216 if (!Mdl
) return NULL
;
219 // This is where the page array starts....
221 MdlPage
= (PPFN_NUMBER
)(Mdl
+ 1);
224 // Lock the PFN database
226 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
229 // Are we looking for any pages, without discriminating?
231 if ((LowPage
== 0) && (HighPage
== MmHighestPhysicalPage
))
234 // Well then, let's go shopping
236 while (PagesFound
< PageCount
)
239 MI_SET_USAGE(MI_USAGE_MDL
);
240 MI_SET_PROCESS2("Kernel");
241 Page
= MiRemoveAnyPage(0);
244 /* This is not good... hopefully we have at least SOME pages */
249 /* Grab the page entry for it */
250 Pfn1
= MiGetPfnEntry(Page
);
253 // Make sure it's really free
255 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
257 /* Now setup the page and mark it */
258 Pfn1
->u3
.e2
.ReferenceCount
= 1;
259 Pfn1
->u2
.ShareCount
= 1;
260 MI_SET_PFN_DELETED(Pfn1
);
261 Pfn1
->u4
.PteFrame
= 0x1FFEDCB;
262 Pfn1
->u3
.e1
.StartOfAllocation
= 1;
263 Pfn1
->u3
.e1
.EndOfAllocation
= 1;
264 Pfn1
->u4
.VerifierAllocation
= 0;
267 // Save it into the MDL
269 *MdlPage
++ = MiGetPfnEntryIndex(Pfn1
);
276 // You want specific range of pages. We'll do this in two runs
278 for (LookForZeroedPages
= 1; LookForZeroedPages
>= 0; LookForZeroedPages
--)
281 // Scan the range you specified
283 for (Page
= LowPage
; Page
< HighPage
; Page
++)
286 // Get the PFN entry for this page
288 Pfn1
= MiGetPfnEntry(Page
);
292 // Make sure it's free and if this is our first pass, zeroed
294 if (MiIsPfnInUse(Pfn1
)) continue;
295 if ((Pfn1
->u3
.e1
.PageLocation
== ZeroedPageList
) != LookForZeroedPages
) continue;
297 /* Remove the page from the free or zero list */
298 ASSERT(Pfn1
->u3
.e1
.ReadInProgress
== 0);
299 MI_SET_USAGE(MI_USAGE_MDL
);
300 MI_SET_PROCESS2("Kernel");
301 MiUnlinkFreeOrZeroedPage(Pfn1
);
306 ASSERT(Pfn1
->u3
.e2
.ReferenceCount
== 0);
309 // Now setup the page and mark it
311 Pfn1
->u3
.e2
.ReferenceCount
= 1;
312 Pfn1
->u2
.ShareCount
= 1;
313 MI_SET_PFN_DELETED(Pfn1
);
314 Pfn1
->u4
.PteFrame
= 0x1FFEDCB;
315 Pfn1
->u3
.e1
.StartOfAllocation
= 1;
316 Pfn1
->u3
.e1
.EndOfAllocation
= 1;
317 Pfn1
->u4
.VerifierAllocation
= 0;
320 // Save this page into the MDL
323 if (++PagesFound
== PageCount
) break;
327 // If the first pass was enough, don't keep going, otherwise, go again
329 if (PagesFound
== PageCount
) break;
334 // Now release the PFN count
336 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
339 // We might've found less pages, but not more ;-)
341 if (PagesFound
!= PageCount
) ASSERT(PagesFound
< PageCount
);
345 // If we didn' tfind any pages at all, fail
347 DPRINT1("NO MDL PAGES!\n");
348 ExFreePoolWithTag(Mdl
, TAG_MDL
);
353 // Write out how many pages we found
355 Mdl
->ByteCount
= (ULONG
)(PagesFound
<< PAGE_SHIFT
);
358 // Terminate the MDL array if there's certain missing pages
360 if (PagesFound
!= PageCount
) *MdlPage
= LIST_HEAD
;
363 // Now go back and loop over all the MDL pages
365 MdlPage
= (PPFN_NUMBER
)(Mdl
+ 1);
366 LastMdlPage
= MdlPage
+ PagesFound
;
367 while (MdlPage
< LastMdlPage
)
370 // Check if we've reached the end
373 if (Page
== LIST_HEAD
) break;
376 // Get the PFN entry for the page and check if we should zero it out
378 Pfn1
= MiGetPfnEntry(Page
);
380 if (Pfn1
->u3
.e1
.PageLocation
!= ZeroedPageList
) MiZeroPhysicalPage(Page
);
381 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
385 // We're done, mark the pages as locked
388 Mdl
->MdlFlags
|= MDL_PAGES_LOCKED
;
394 MmSetRmapListHeadPage(PFN_NUMBER Pfn
, PMM_RMAP_ENTRY ListHead
)
399 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
400 Pfn1
= MiGetPfnEntry(Pfn
);
402 ASSERT_IS_ROS_PFN(Pfn1
);
406 /* Should not be trying to insert an RMAP for a non-active page */
407 ASSERT(MiIsPfnInUse(Pfn1
) == TRUE
);
409 /* Set the list head address */
410 MI_GET_ROS_DATA(Pfn1
)->RmapListHead
= ListHead
;
414 /* ReactOS semantics dictate the page is STILL active right now */
415 ASSERT(MiIsPfnInUse(Pfn1
) == TRUE
);
417 /* In this case, the RMAP is actually being removed, so clear field */
418 MI_GET_ROS_DATA(Pfn1
)->RmapListHead
= NULL
;
420 /* ReactOS semantics will now release the page, which will make it free and enter a colored list */
423 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
428 MmGetRmapListHeadPage(PFN_NUMBER Pfn
)
431 PMM_RMAP_ENTRY ListHead
;
434 /* Lock PFN database */
435 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
438 Pfn1
= MiGetPfnEntry(Pfn
);
440 ASSERT_IS_ROS_PFN(Pfn1
);
442 /* Get the list head */
443 ListHead
= MI_GET_ROS_DATA(Pfn1
)->RmapListHead
;
445 /* Should not have an RMAP for a non-active page */
446 ASSERT(MiIsPfnInUse(Pfn1
) == TRUE
);
448 /* Release PFN database and return rmap list head */
449 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
455 MmSetSavedSwapEntryPage(PFN_NUMBER Pfn
, SWAPENTRY SwapEntry
)
460 Page
= MiGetPfnEntry(Pfn
);
462 ASSERT_IS_ROS_PFN(Page
);
464 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
465 MI_GET_ROS_DATA(Page
)->SwapEntry
= SwapEntry
;
466 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
471 MmGetSavedSwapEntryPage(PFN_NUMBER Pfn
)
477 Page
= MiGetPfnEntry(Pfn
);
479 ASSERT_IS_ROS_PFN(Page
);
481 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
482 SwapEntry
= MI_GET_ROS_DATA(Page
)->SwapEntry
;
483 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
490 MmReferencePage(PFN_NUMBER Pfn
)
494 DPRINT("MmReferencePage(PysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
496 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL
);
498 ASSERT(Pfn
<= MmHighestPhysicalPage
);
500 Page
= MiGetPfnEntry(Pfn
);
502 ASSERT_IS_ROS_PFN(Page
);
504 ASSERT(Page
->u3
.e2
.ReferenceCount
!= 0);
505 Page
->u3
.e2
.ReferenceCount
++;
510 MmGetReferenceCountPage(PFN_NUMBER Pfn
)
516 DPRINT("MmGetReferenceCountPage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
518 oldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
519 Page
= MiGetPfnEntry(Pfn
);
521 ASSERT_IS_ROS_PFN(Page
);
523 RCount
= Page
->u3
.e2
.ReferenceCount
;
525 KeReleaseQueuedSpinLock(LockQueuePfnLock
, oldIrql
);
531 MmIsPageInUse(PFN_NUMBER Pfn
)
533 return MiIsPfnInUse(MiGetPfnEntry(Pfn
));
538 MmDereferencePage(PFN_NUMBER Pfn
)
541 DPRINT("MmDereferencePage(PhysicalAddress %x)\n", Pfn
<< PAGE_SHIFT
);
543 Page
= MiGetPfnEntry(Pfn
);
545 ASSERT_IS_ROS_PFN(Page
);
547 ASSERT(Page
->u3
.e2
.ReferenceCount
!= 0);
548 Page
->u3
.e2
.ReferenceCount
--;
549 if (Page
->u3
.e2
.ReferenceCount
== 0)
551 /* Mark the page temporarily as valid, we're going to make it free soon */
552 Page
->u3
.e1
.PageLocation
= ActiveAndValid
;
554 /* It's not a ROS PFN anymore */
555 Page
->u4
.AweAllocation
= FALSE
;
556 ExFreePool(MI_GET_ROS_DATA(Page
));
559 /* Bring it back into the free list */
560 DPRINT("Legacy free: %lx\n", Pfn
);
561 MiInsertPageInFreeList(Pfn
);
567 MmAllocPage(ULONG Type
)
569 PFN_NUMBER PfnOffset
;
572 PfnOffset
= MiRemoveZeroPage(MI_GET_NEXT_COLOR());
576 DPRINT1("MmAllocPage(): Out of memory\n");
580 DPRINT("Legacy allocate: %lx\n", PfnOffset
);
581 Pfn1
= MiGetPfnEntry(PfnOffset
);
582 Pfn1
->u3
.e2
.ReferenceCount
= 1;
583 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
585 /* This marks the PFN as a ReactOS PFN */
586 Pfn1
->u4
.AweAllocation
= TRUE
;
588 /* Allocate the extra ReactOS Data and zero it out */
589 Pfn1
->RosMmData
= (LONG
)ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMROSPFN
), 'RsPf');
590 ASSERT(MI_GET_ROS_DATA(Pfn1
) != NULL
);
591 ASSERT_IS_ROS_PFN(Pfn1
);
592 MI_GET_ROS_DATA(Pfn1
)->SwapEntry
= 0;
593 MI_GET_ROS_DATA(Pfn1
)->RmapListHead
= NULL
;