Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenfreelist.c
Go to the documentation of this file.
00001 /* 00002 * COPYRIGHT: See COPYING in the top level directory 00003 * PROJECT: ReactOS kernel 00004 * FILE: ntoskrnl/mm/freelist.c 00005 * PURPOSE: Handle the list of free physical pages 00006 * 00007 * PROGRAMMERS: David Welch (welch@cwcom.net) 00008 * Robert Bergkvist 00009 */ 00010 00011 /* INCLUDES ****************************************************************/ 00012 00013 #include <ntoskrnl.h> 00014 #define NDEBUG 00015 #include <debug.h> 00016 00017 #if defined (ALLOC_PRAGMA) 00018 #pragma alloc_text(INIT, MmInitializePageList) 00019 #endif 00020 00021 #define MODULE_INVOLVED_IN_ARM3 00022 #include "ARM3/miarm.h" 00023 00024 /* GLOBALS ****************************************************************/ 00025 00026 // ReactOS to NT Physical Page Descriptor Entry Legacy Mapping Definitions 00027 #define PHYSICAL_PAGE MMPFN 00028 #define PPHYSICAL_PAGE PMMPFN 00029 00030 PPHYSICAL_PAGE MmPfnDatabase; 00031 00032 PFN_NUMBER MmAvailablePages; 00033 PFN_NUMBER MmResidentAvailablePages; 00034 PFN_NUMBER MmResidentAvailableAtInit; 00035 00036 SIZE_T MmTotalCommittedPages; 00037 SIZE_T MmSharedCommit; 00038 SIZE_T MmDriverCommit; 00039 SIZE_T MmProcessCommit; 00040 SIZE_T MmPagedPoolCommit; 00041 SIZE_T MmPeakCommitment; 00042 SIZE_T MmtotalCommitLimitMaximum; 00043 00044 static RTL_BITMAP MiUserPfnBitMap; 00045 00046 /* FUNCTIONS *************************************************************/ 00047 00048 VOID 00049 NTAPI 00050 MiInitializeUserPfnBitmap(VOID) 00051 { 00052 PVOID Bitmap; 00053 00054 /* Allocate enough buffer for the PFN bitmap and align it on 32-bits */ 00055 Bitmap = ExAllocatePoolWithTag(NonPagedPool, 00056 (((MmHighestPhysicalPage + 1) + 31) / 32) * 4, 00057 ' mM'); 00058 ASSERT(Bitmap); 00059 00060 /* Initialize it and clear all the bits to begin with */ 00061 RtlInitializeBitMap(&MiUserPfnBitMap, 00062 Bitmap, 00063 (ULONG)MmHighestPhysicalPage + 1); 00064 RtlClearAllBits(&MiUserPfnBitMap); 00065 } 00066 00067 PFN_NUMBER 00068 NTAPI 00069 MmGetLRUFirstUserPage(VOID) 00070 { 00071 ULONG Position; 00072 KIRQL OldIrql; 00073 00074 /* Find the first user page */ 00075 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00076 Position = RtlFindSetBits(&MiUserPfnBitMap, 1, 0); 00077 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00078 if (Position == 0xFFFFFFFF) return 0; 00079 00080 /* Return it */ 00081 ASSERT(Position != 0); 00082 ASSERT_IS_ROS_PFN(MiGetPfnEntry(Position)); 00083 return Position; 00084 } 00085 00086 VOID 00087 NTAPI 00088 MmInsertLRULastUserPage(PFN_NUMBER Pfn) 00089 { 00090 KIRQL OldIrql; 00091 00092 /* Set the page as a user page */ 00093 ASSERT(Pfn != 0); 00094 ASSERT_IS_ROS_PFN(MiGetPfnEntry(Pfn)); 00095 ASSERT(!RtlCheckBit(&MiUserPfnBitMap, (ULONG)Pfn)); 00096 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00097 RtlSetBit(&MiUserPfnBitMap, (ULONG)Pfn); 00098 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00099 } 00100 00101 PFN_NUMBER 00102 NTAPI 00103 MmGetLRUNextUserPage(PFN_NUMBER PreviousPfn) 00104 { 00105 ULONG Position; 00106 KIRQL OldIrql; 00107 00108 /* Find the next user page */ 00109 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00110 Position = RtlFindSetBits(&MiUserPfnBitMap, 1, (ULONG)PreviousPfn + 1); 00111 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00112 if (Position == 0xFFFFFFFF) return 0; 00113 00114 /* Return it */ 00115 ASSERT(Position != 0); 00116 ASSERT_IS_ROS_PFN(MiGetPfnEntry(Position)); 00117 return Position; 00118 } 00119 00120 VOID 00121 NTAPI 00122 MmRemoveLRUUserPage(PFN_NUMBER Page) 00123 { 00124 KIRQL OldIrql; 00125 00126 /* Unset the page as a user page */ 00127 ASSERT(Page != 0); 00128 ASSERT_IS_ROS_PFN(MiGetPfnEntry(Page)); 00129 ASSERT(RtlCheckBit(&MiUserPfnBitMap, (ULONG)Page)); 00130 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00131 RtlClearBit(&MiUserPfnBitMap, (ULONG)Page); 00132 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00133 } 00134 00135 BOOLEAN 00136 NTAPI 00137 MiIsPfnFree(IN PMMPFN Pfn1) 00138 { 00139 /* Must be a free or zero page, with no references, linked */ 00140 return ((Pfn1->u3.e1.PageLocation <= StandbyPageList) && 00141 (Pfn1->u1.Flink) && 00142 (Pfn1->u2.Blink) && 00143 !(Pfn1->u3.e2.ReferenceCount)); 00144 } 00145 00146 BOOLEAN 00147 NTAPI 00148 MiIsPfnInUse(IN PMMPFN Pfn1) 00149 { 00150 /* Standby list or higher, unlinked, and with references */ 00151 return !MiIsPfnFree(Pfn1); 00152 } 00153 00154 PMDL 00155 NTAPI 00156 MiAllocatePagesForMdl(IN PHYSICAL_ADDRESS LowAddress, 00157 IN PHYSICAL_ADDRESS HighAddress, 00158 IN PHYSICAL_ADDRESS SkipBytes, 00159 IN SIZE_T TotalBytes, 00160 IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute, 00161 IN ULONG MdlFlags) 00162 { 00163 PMDL Mdl; 00164 PFN_NUMBER PageCount, LowPage, HighPage, SkipPages, PagesFound = 0, Page; 00165 PPFN_NUMBER MdlPage, LastMdlPage; 00166 KIRQL OldIrql; 00167 PPHYSICAL_PAGE Pfn1; 00168 INT LookForZeroedPages; 00169 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 00170 DPRINT1("ARM3-DEBUG: Being called with %I64x %I64x %I64x %lx %d %d\n", LowAddress, HighAddress, SkipBytes, TotalBytes, CacheAttribute, MdlFlags); 00171 00172 // 00173 // Convert the low address into a PFN 00174 // 00175 LowPage = (PFN_NUMBER)(LowAddress.QuadPart >> PAGE_SHIFT); 00176 00177 // 00178 // Convert, and normalize, the high address into a PFN 00179 // 00180 HighPage = (PFN_NUMBER)(HighAddress.QuadPart >> PAGE_SHIFT); 00181 if (HighPage > MmHighestPhysicalPage) HighPage = MmHighestPhysicalPage; 00182 00183 // 00184 // Validate skipbytes and convert them into pages 00185 // 00186 if (BYTE_OFFSET(SkipBytes.LowPart)) return NULL; 00187 SkipPages = (PFN_NUMBER)(SkipBytes.QuadPart >> PAGE_SHIFT); 00188 00189 /* This isn't supported at all */ 00190 if (SkipPages) DPRINT1("WARNING: Caller requesting SkipBytes, MDL might be mismatched\n"); 00191 00192 // 00193 // Now compute the number of pages the MDL will cover 00194 // 00195 PageCount = (PFN_NUMBER)ADDRESS_AND_SIZE_TO_SPAN_PAGES(0, TotalBytes); 00196 do 00197 { 00198 // 00199 // Try creating an MDL for these many pages 00200 // 00201 Mdl = MmCreateMdl(NULL, NULL, PageCount << PAGE_SHIFT); 00202 if (Mdl) break; 00203 00204 // 00205 // This function is not required to return the amount of pages requested 00206 // In fact, it can return as little as 1 page, and callers are supposed 00207 // to deal with this scenario. So re-attempt the allocation with less 00208 // pages than before, and see if it worked this time. 00209 // 00210 PageCount -= (PageCount >> 4); 00211 } while (PageCount); 00212 00213 // 00214 // Wow, not even a single page was around! 00215 // 00216 if (!Mdl) return NULL; 00217 00218 // 00219 // This is where the page array starts.... 00220 // 00221 MdlPage = (PPFN_NUMBER)(Mdl + 1); 00222 00223 // 00224 // Lock the PFN database 00225 // 00226 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00227 00228 // 00229 // Are we looking for any pages, without discriminating? 00230 // 00231 if ((LowPage == 0) && (HighPage == MmHighestPhysicalPage)) 00232 { 00233 // 00234 // Well then, let's go shopping 00235 // 00236 while (PagesFound < PageCount) 00237 { 00238 /* Grab a page */ 00239 MI_SET_USAGE(MI_USAGE_MDL); 00240 MI_SET_PROCESS2("Kernel"); 00241 Page = MiRemoveAnyPage(0); 00242 if (Page == 0) 00243 { 00244 /* This is not good... hopefully we have at least SOME pages */ 00245 ASSERT(PagesFound); 00246 break; 00247 } 00248 00249 /* Grab the page entry for it */ 00250 Pfn1 = MiGetPfnEntry(Page); 00251 00252 // 00253 // Make sure it's really free 00254 // 00255 ASSERT(Pfn1->u3.e2.ReferenceCount == 0); 00256 00257 /* Now setup the page and mark it */ 00258 Pfn1->u3.e2.ReferenceCount = 1; 00259 Pfn1->u2.ShareCount = 1; 00260 MI_SET_PFN_DELETED(Pfn1); 00261 Pfn1->u4.PteFrame = 0x1FFEDCB; 00262 Pfn1->u3.e1.StartOfAllocation = 1; 00263 Pfn1->u3.e1.EndOfAllocation = 1; 00264 Pfn1->u4.VerifierAllocation = 0; 00265 00266 // 00267 // Save it into the MDL 00268 // 00269 *MdlPage++ = MiGetPfnEntryIndex(Pfn1); 00270 PagesFound++; 00271 } 00272 } 00273 else 00274 { 00275 // 00276 // You want specific range of pages. We'll do this in two runs 00277 // 00278 for (LookForZeroedPages = 1; LookForZeroedPages >= 0; LookForZeroedPages--) 00279 { 00280 // 00281 // Scan the range you specified 00282 // 00283 for (Page = LowPage; Page < HighPage; Page++) 00284 { 00285 // 00286 // Get the PFN entry for this page 00287 // 00288 Pfn1 = MiGetPfnEntry(Page); 00289 ASSERT(Pfn1); 00290 00291 // 00292 // Make sure it's free and if this is our first pass, zeroed 00293 // 00294 if (MiIsPfnInUse(Pfn1)) continue; 00295 if ((Pfn1->u3.e1.PageLocation == ZeroedPageList) != LookForZeroedPages) continue; 00296 00297 /* Remove the page from the free or zero list */ 00298 ASSERT(Pfn1->u3.e1.ReadInProgress == 0); 00299 MI_SET_USAGE(MI_USAGE_MDL); 00300 MI_SET_PROCESS2("Kernel"); 00301 MiUnlinkFreeOrZeroedPage(Pfn1); 00302 00303 // 00304 // Sanity checks 00305 // 00306 ASSERT(Pfn1->u3.e2.ReferenceCount == 0); 00307 00308 // 00309 // Now setup the page and mark it 00310 // 00311 Pfn1->u3.e2.ReferenceCount = 1; 00312 Pfn1->u2.ShareCount = 1; 00313 MI_SET_PFN_DELETED(Pfn1); 00314 Pfn1->u4.PteFrame = 0x1FFEDCB; 00315 Pfn1->u3.e1.StartOfAllocation = 1; 00316 Pfn1->u3.e1.EndOfAllocation = 1; 00317 Pfn1->u4.VerifierAllocation = 0; 00318 00319 // 00320 // Save this page into the MDL 00321 // 00322 *MdlPage++ = Page; 00323 if (++PagesFound == PageCount) break; 00324 } 00325 00326 // 00327 // If the first pass was enough, don't keep going, otherwise, go again 00328 // 00329 if (PagesFound == PageCount) break; 00330 } 00331 } 00332 00333 // 00334 // Now release the PFN count 00335 // 00336 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00337 00338 // 00339 // We might've found less pages, but not more ;-) 00340 // 00341 if (PagesFound != PageCount) ASSERT(PagesFound < PageCount); 00342 if (!PagesFound) 00343 { 00344 // 00345 // If we didn' tfind any pages at all, fail 00346 // 00347 DPRINT1("NO MDL PAGES!\n"); 00348 ExFreePool(Mdl); 00349 return NULL; 00350 } 00351 00352 // 00353 // Write out how many pages we found 00354 // 00355 Mdl->ByteCount = (ULONG)(PagesFound << PAGE_SHIFT); 00356 00357 // 00358 // Terminate the MDL array if there's certain missing pages 00359 // 00360 if (PagesFound != PageCount) *MdlPage = LIST_HEAD; 00361 00362 // 00363 // Now go back and loop over all the MDL pages 00364 // 00365 MdlPage = (PPFN_NUMBER)(Mdl + 1); 00366 LastMdlPage = MdlPage + PagesFound; 00367 while (MdlPage < LastMdlPage) 00368 { 00369 // 00370 // Check if we've reached the end 00371 // 00372 Page = *MdlPage++; 00373 if (Page == LIST_HEAD) break; 00374 00375 // 00376 // Get the PFN entry for the page and check if we should zero it out 00377 // 00378 Pfn1 = MiGetPfnEntry(Page); 00379 ASSERT(Pfn1); 00380 if (Pfn1->u3.e1.PageLocation != ZeroedPageList) MiZeroPhysicalPage(Page); 00381 Pfn1->u3.e1.PageLocation = ActiveAndValid; 00382 } 00383 00384 // 00385 // We're done, mark the pages as locked 00386 // 00387 Mdl->Process = NULL; 00388 Mdl->MdlFlags |= MDL_PAGES_LOCKED; 00389 return Mdl; 00390 } 00391 00392 VOID 00393 NTAPI 00394 MmSetRmapListHeadPage(PFN_NUMBER Pfn, PMM_RMAP_ENTRY ListHead) 00395 { 00396 KIRQL oldIrql; 00397 PMMPFN Pfn1; 00398 00399 oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00400 Pfn1 = MiGetPfnEntry(Pfn); 00401 ASSERT(Pfn1); 00402 ASSERT_IS_ROS_PFN(Pfn1); 00403 00404 if (ListHead) 00405 { 00406 /* Should not be trying to insert an RMAP for a non-active page */ 00407 ASSERT(MiIsPfnInUse(Pfn1) == TRUE); 00408 00409 /* Set the list head address */ 00410 MI_GET_ROS_DATA(Pfn1)->RmapListHead = ListHead; 00411 } 00412 else 00413 { 00414 /* ReactOS semantics dictate the page is STILL active right now */ 00415 ASSERT(MiIsPfnInUse(Pfn1) == TRUE); 00416 00417 /* In this case, the RMAP is actually being removed, so clear field */ 00418 MI_GET_ROS_DATA(Pfn1)->RmapListHead = NULL; 00419 00420 /* ReactOS semantics will now release the page, which will make it free and enter a colored list */ 00421 } 00422 00423 KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql); 00424 } 00425 00426 PMM_RMAP_ENTRY 00427 NTAPI 00428 MmGetRmapListHeadPage(PFN_NUMBER Pfn) 00429 { 00430 KIRQL oldIrql; 00431 PMM_RMAP_ENTRY ListHead; 00432 PMMPFN Pfn1; 00433 00434 /* Lock PFN database */ 00435 oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00436 00437 /* Get the entry */ 00438 Pfn1 = MiGetPfnEntry(Pfn); 00439 ASSERT(Pfn1); 00440 ASSERT_IS_ROS_PFN(Pfn1); 00441 00442 /* Get the list head */ 00443 ListHead = MI_GET_ROS_DATA(Pfn1)->RmapListHead; 00444 00445 /* Should not have an RMAP for a non-active page */ 00446 ASSERT(MiIsPfnInUse(Pfn1) == TRUE); 00447 00448 /* Release PFN database and return rmap list head */ 00449 KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql); 00450 return ListHead; 00451 } 00452 00453 VOID 00454 NTAPI 00455 MmSetSavedSwapEntryPage(PFN_NUMBER Pfn, SWAPENTRY SwapEntry) 00456 { 00457 KIRQL oldIrql; 00458 PPHYSICAL_PAGE Page; 00459 00460 Page = MiGetPfnEntry(Pfn); 00461 ASSERT(Page); 00462 ASSERT_IS_ROS_PFN(Page); 00463 00464 oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00465 MI_GET_ROS_DATA(Page)->SwapEntry = SwapEntry; 00466 KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql); 00467 } 00468 00469 SWAPENTRY 00470 NTAPI 00471 MmGetSavedSwapEntryPage(PFN_NUMBER Pfn) 00472 { 00473 SWAPENTRY SwapEntry; 00474 KIRQL oldIrql; 00475 PPHYSICAL_PAGE Page; 00476 00477 Page = MiGetPfnEntry(Pfn); 00478 ASSERT(Page); 00479 ASSERT_IS_ROS_PFN(Page); 00480 00481 oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00482 SwapEntry = MI_GET_ROS_DATA(Page)->SwapEntry; 00483 KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql); 00484 00485 return(SwapEntry); 00486 } 00487 00488 VOID 00489 NTAPI 00490 MmReferencePage(PFN_NUMBER Pfn) 00491 { 00492 PPHYSICAL_PAGE Page; 00493 00494 DPRINT("MmReferencePage(PysicalAddress %x)\n", Pfn << PAGE_SHIFT); 00495 00496 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); 00497 ASSERT(Pfn != 0); 00498 ASSERT(Pfn <= MmHighestPhysicalPage); 00499 00500 Page = MiGetPfnEntry(Pfn); 00501 ASSERT(Page); 00502 ASSERT_IS_ROS_PFN(Page); 00503 00504 ASSERT(Page->u3.e2.ReferenceCount != 0); 00505 Page->u3.e2.ReferenceCount++; 00506 } 00507 00508 ULONG 00509 NTAPI 00510 MmGetReferenceCountPage(PFN_NUMBER Pfn) 00511 { 00512 KIRQL oldIrql; 00513 ULONG RCount; 00514 PPHYSICAL_PAGE Page; 00515 00516 DPRINT("MmGetReferenceCountPage(PhysicalAddress %x)\n", Pfn << PAGE_SHIFT); 00517 00518 oldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00519 Page = MiGetPfnEntry(Pfn); 00520 ASSERT(Page); 00521 ASSERT_IS_ROS_PFN(Page); 00522 00523 RCount = Page->u3.e2.ReferenceCount; 00524 00525 KeReleaseQueuedSpinLock(LockQueuePfnLock, oldIrql); 00526 return(RCount); 00527 } 00528 00529 BOOLEAN 00530 NTAPI 00531 MmIsPageInUse(PFN_NUMBER Pfn) 00532 { 00533 return MiIsPfnInUse(MiGetPfnEntry(Pfn)); 00534 } 00535 00536 VOID 00537 NTAPI 00538 MmDereferencePage(PFN_NUMBER Pfn) 00539 { 00540 PPHYSICAL_PAGE Page; 00541 DPRINT("MmDereferencePage(PhysicalAddress %x)\n", Pfn << PAGE_SHIFT); 00542 00543 Page = MiGetPfnEntry(Pfn); 00544 ASSERT(Page); 00545 ASSERT_IS_ROS_PFN(Page); 00546 00547 ASSERT(Page->u3.e2.ReferenceCount != 0); 00548 Page->u3.e2.ReferenceCount--; 00549 if (Page->u3.e2.ReferenceCount == 0) 00550 { 00551 /* Mark the page temporarily as valid, we're going to make it free soon */ 00552 Page->u3.e1.PageLocation = ActiveAndValid; 00553 00554 /* It's not a ROS PFN anymore */ 00555 Page->u4.AweAllocation = FALSE; 00556 ExFreePool(MI_GET_ROS_DATA(Page)); 00557 Page->RosMmData = 0; 00558 00559 /* Bring it back into the free list */ 00560 DPRINT("Legacy free: %lx\n", Pfn); 00561 MiInsertPageInFreeList(Pfn); 00562 } 00563 } 00564 00565 PFN_NUMBER 00566 NTAPI 00567 MmAllocPage(ULONG Type) 00568 { 00569 PFN_NUMBER PfnOffset; 00570 PMMPFN Pfn1; 00571 00572 PfnOffset = MiRemoveZeroPage(MI_GET_NEXT_COLOR()); 00573 00574 if (!PfnOffset) 00575 { 00576 DPRINT1("MmAllocPage(): Out of memory\n"); 00577 return 0; 00578 } 00579 00580 DPRINT("Legacy allocate: %lx\n", PfnOffset); 00581 Pfn1 = MiGetPfnEntry(PfnOffset); 00582 Pfn1->u3.e2.ReferenceCount = 1; 00583 Pfn1->u3.e1.PageLocation = ActiveAndValid; 00584 00585 /* This marks the PFN as a ReactOS PFN */ 00586 Pfn1->u4.AweAllocation = TRUE; 00587 00588 /* Allocate the extra ReactOS Data and zero it out */ 00589 Pfn1->RosMmData = (LONG)ExAllocatePoolWithTag(NonPagedPool, sizeof(MMROSPFN), 'RsPf'); 00590 ASSERT(MI_GET_ROS_DATA(Pfn1) != NULL); 00591 ASSERT_IS_ROS_PFN(Pfn1); 00592 MI_GET_ROS_DATA(Pfn1)->SwapEntry = 0; 00593 MI_GET_ROS_DATA(Pfn1)->RmapListHead = NULL; 00594 00595 return PfnOffset; 00596 } 00597 00598 /* EOF */ Generated on Sun May 27 2012 04:37:38 for ReactOS by
1.7.6.1
|