Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

pfnlist.c

Go to the documentation of this file.
00001 /*++ 00002 00003 Copyright (c) 1989 Microsoft Corporation 00004 00005 Module Name: 00006 00007 pfnlist.c 00008 00009 Abstract: 00010 00011 This module contains the routines to manipulate pages within the 00012 Page Frame Database. 00013 00014 Author: 00015 00016 Lou Perazzoli (loup) 4-Apr-1989 00017 Landy Wang (landyw) 02-June-1997 00018 00019 Revision History: 00020 00021 --*/ 00022 #include "mi.h" 00023 00024 #define MM_LOW_LIMIT 2 00025 #define MM_HIGH_LIMIT 19 00026 00027 KEVENT MmAvailablePagesEventHigh; 00028 00029 ULONG MmTransitionPrivatePages; 00030 ULONG MmTransitionSharedPages; 00031 00032 #define MI_TALLY_TRANSITION_PAGE_ADDITION(Pfn) \ 00033 if (Pfn->u3.e1.PrototypePte) { \ 00034 MmTransitionSharedPages += 1; \ 00035 } \ 00036 else { \ 00037 MmTransitionPrivatePages += 1; \ 00038 } \ 00039 ASSERT (MmTransitionPrivatePages + MmTransitionSharedPages == MmStandbyPageListHead.Total + MmModifiedPageListHead.Total + MmModifiedNoWritePageListHead.Total); 00040 00041 #define MI_TALLY_TRANSITION_PAGE_REMOVAL(Pfn) \ 00042 if (Pfn->u3.e1.PrototypePte) { \ 00043 MmTransitionSharedPages -= 1; \ 00044 } \ 00045 else { \ 00046 MmTransitionPrivatePages -= 1; \ 00047 } \ 00048 ASSERT (MmTransitionPrivatePages + MmTransitionSharedPages == MmStandbyPageListHead.Total + MmModifiedPageListHead.Total + MmModifiedNoWritePageListHead.Total); 00049 00050 VOID 00051 MiRemovePageByColor ( 00052 IN PFN_NUMBER Page, 00053 IN ULONG PageColor 00054 ); 00055 00056 00057 VOID 00058 FASTCALL 00059 MiInsertPageInList ( 00060 IN PMMPFNLIST ListHead, 00061 IN PFN_NUMBER PageFrameIndex 00062 ) 00063 00064 /*++ 00065 00066 Routine Description: 00067 00068 This procedure inserts a page at the end of the specified list (free, 00069 standby, bad, zeroed, modified). 00070 00071 00072 Arguments: 00073 00074 ListHead - Supplies the list of the list in which to insert the 00075 specified physical page. 00076 00077 PageFrameIndex - Supplies the physical page number to insert in the 00078 list. 00079 00080 Return Value: 00081 00082 none. 00083 00084 Environment: 00085 00086 Must be holding the PFN database mutex with APCs disabled. 00087 00088 --*/ 00089 00090 { 00091 PFN_NUMBER last; 00092 PMMPFN Pfn1; 00093 PMMPFN Pfn2; 00094 ULONG Color; 00095 00096 MM_PFN_LOCK_ASSERT(); 00097 ASSERT ((PageFrameIndex != 0) && 00098 (PageFrameIndex <= MmHighestPhysicalPage) && 00099 (PageFrameIndex >= MmLowestPhysicalPage)); 00100 00101 // 00102 // Check to ensure the reference count for the page is zero. 00103 // 00104 00105 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 00106 00107 ASSERT (Pfn1->u3.e1.LockCharged == 0); 00108 00109 PERFINFO_INSERTINLIST(PageFrameIndex, ListHead); 00110 00111 #if DBG 00112 if (MmDebug & MM_DBG_PAGE_REF_COUNT) { 00113 00114 PMMPTE PointerPte; 00115 KIRQL OldIrql = 99; 00116 00117 if ((ListHead->ListName == StandbyPageList) || 00118 (ListHead->ListName == ModifiedPageList)) { 00119 00120 if ((Pfn1->u3.e1.PrototypePte == 1) && 00121 (MmIsAddressValid (Pfn1->PteAddress))) { 00122 PointerPte = Pfn1->PteAddress; 00123 } else { 00124 00125 // 00126 // The page containing the prototype PTE is not valid, 00127 // map the page into hyperspace and reference it that way. 00128 // 00129 00130 PointerPte = MiMapPageInHyperSpace (Pfn1->PteFrame, &OldIrql); 00131 PointerPte = (PMMPTE)((PCHAR)PointerPte + 00132 MiGetByteOffset(Pfn1->PteAddress)); 00133 } 00134 00135 ASSERT ((MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerPte) == PageFrameIndex) || 00136 (MI_GET_PAGE_FRAME_FROM_PTE (PointerPte) == PageFrameIndex)); 00137 ASSERT (PointerPte->u.Soft.Transition == 1); 00138 ASSERT (PointerPte->u.Soft.Prototype == 0); 00139 if (OldIrql != 99) { 00140 MiUnmapPageInHyperSpace (OldIrql) 00141 } 00142 } 00143 } 00144 #endif 00145 00146 #if PFN_CONSISTENCY 00147 if (ListHead == &MmFreePageListHead) { 00148 if (Pfn1->u2.ShareCount != 0) { 00149 KeBugCheckEx (PFN_LIST_CORRUPT, 00150 0x91, 00151 PageFrameIndex, 00152 Pfn1->u2.ShareCount, 00153 Pfn1->u3.e2.ReferenceCount); 00154 } 00155 } 00156 else if (ListHead == &MmZeroedPageListHead) { 00157 if (Pfn1->u2.ShareCount != 0) { 00158 KeBugCheckEx (PFN_LIST_CORRUPT, 00159 0x92, 00160 PageFrameIndex, 00161 Pfn1->u2.ShareCount, 00162 Pfn1->u3.e2.ReferenceCount); 00163 } 00164 } 00165 #endif 00166 00167 #if DBG 00168 if ((ListHead->ListName == StandbyPageList) || 00169 (ListHead->ListName == ModifiedPageList)) { 00170 if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) && 00171 (Pfn1->OriginalPte.u.Soft.Transition == 1)) { 00172 KeBugCheckEx (MEMORY_MANAGEMENT, 0x8888, 0,0,0); 00173 } 00174 } 00175 #endif 00176 00177 ASSERT (Pfn1->u3.e2.ReferenceCount == 0); 00178 00179 ListHead->Total += 1; // One more page on the list. 00180 00181 // 00182 // On MIPS R4000 modified pages destined for the paging file are 00183 // kept on separate lists which group pages of the same color 00184 // together 00185 // 00186 00187 if (ListHead == &MmModifiedPageListHead) { 00188 00189 #if PFN_CONSISTENCY 00190 if (Pfn1->u2.ShareCount != 0) { 00191 KeBugCheckEx (PFN_LIST_CORRUPT, 00192 0x90, 00193 PageFrameIndex, 00194 Pfn1->u2.ShareCount, 00195 Pfn1->u3.e2.ReferenceCount); 00196 } 00197 #endif 00198 00199 if (Pfn1->OriginalPte.u.Soft.Prototype == 0) { 00200 00201 // 00202 // This page is destined for the paging file (not 00203 // a mapped file). Change the list head to the 00204 // appropriate colored list head. 00205 // 00206 00207 ListHead = &MmModifiedPageListByColor [Pfn1->u3.e1.PageColor]; 00208 ListHead->Total += 1; 00209 MmTotalPagesForPagingFile += 1; 00210 } 00211 else { 00212 00213 // 00214 // This page is destined for a mapped file (not 00215 // the paging file). If there are no other pages currently 00216 // destined for the mapped file, start our timer so that we can 00217 // ensure that these pages make it to disk even if we don't pile 00218 // up enough of them to trigger the modified page writer or need 00219 // the memory. If we don't do this here, then for this scenario, 00220 // only an orderly system shutdown will write them out (days, 00221 // weeks, months or years later) and any power out in between 00222 // means we'll have lost the data. 00223 // 00224 00225 if (ListHead->Total - MmTotalPagesForPagingFile == 1) { 00226 00227 // 00228 // Start the DPC timer because we're the first on the list. 00229 // 00230 00231 if (MiTimerPending == FALSE) { 00232 MiTimerPending = TRUE; 00233 00234 (VOID) KeSetTimerEx( &MiModifiedPageWriterTimer, MiModifiedPageLife, 0, &MiModifiedPageWriterTimerDpc ); 00235 } 00236 } 00237 } 00238 } 00239 else if ((Pfn1->u3.e1.RemovalRequested == 1) && 00240 (ListHead->ListName <= StandbyPageList)) { 00241 00242 ListHead->Total -= 1; // Undo previous increment 00243 00244 if (ListHead->ListName == StandbyPageList) { 00245 Pfn1->u3.e1.PageLocation = StandbyPageList; 00246 MiRestoreTransitionPte (PageFrameIndex); 00247 } 00248 00249 ListHead = MmPageLocationList[BadPageList]; 00250 ListHead->Total += 1; // One more page on the list. 00251 } 00252 00253 00254 last = ListHead->Blink; 00255 if (last == MM_EMPTY_LIST) { 00256 00257 // 00258 // List is empty add the page to the ListHead. 00259 // 00260 00261 ListHead->Flink = PageFrameIndex; 00262 } else { 00263 Pfn2 = MI_PFN_ELEMENT (last); 00264 Pfn2->u1.Flink = PageFrameIndex; 00265 } 00266 00267 ListHead->Blink = PageFrameIndex; 00268 Pfn1->u1.Flink = MM_EMPTY_LIST; 00269 Pfn1->u2.Blink = last; 00270 Pfn1->u3.e1.PageLocation = ListHead->ListName; 00271 00272 // 00273 // If the page was placed on the free, standby or zeroed list, 00274 // update the count of usable pages in the system. If the count 00275 // transitions from 0 to 1, the event associated with available 00276 // pages should become true. 00277 // 00278 00279 if (ListHead->ListName <= StandbyPageList) { 00280 MmAvailablePages += 1; 00281 00282 // 00283 // A page has just become available, check to see if the 00284 // page wait events should be signalled. 00285 // 00286 00287 if (MmAvailablePages == MM_LOW_LIMIT) { 00288 KeSetEvent (&MmAvailablePagesEvent, 0, FALSE); 00289 } else if (MmAvailablePages == MM_HIGH_LIMIT) { 00290 KeSetEvent (&MmAvailablePagesEventHigh, 0, FALSE); 00291 } 00292 00293 if (ListHead->ListName <= FreePageList) { 00294 00295 ASSERT (Pfn1->u3.e1.InPageError == 0); 00296 00297 // 00298 // We are adding a page to the free or zeroed page list. 00299 // Add the page to the end of the correct colored page list. 00300 // 00301 00302 Color = MI_GET_SECONDARY_COLOR (PageFrameIndex, Pfn1); 00303 ASSERT (Pfn1->u3.e1.PageColor == MI_GET_COLOR_FROM_SECONDARY(Color)); 00304 00305 if (MmFreePagesByColor[ListHead->ListName][Color].Flink == 00306 MM_EMPTY_LIST) { 00307 00308 // 00309 // This list is empty, add this as the first and last 00310 // entry. 00311 // 00312 00313 MmFreePagesByColor[ListHead->ListName][Color].Flink = 00314 PageFrameIndex; 00315 MmFreePagesByColor[ListHead->ListName][Color].Blink = 00316 (PVOID)Pfn1; 00317 } else { 00318 Pfn2 = (PMMPFN)MmFreePagesByColor[ListHead->ListName][Color].Blink; 00319 Pfn2->OriginalPte.u.Long = PageFrameIndex; 00320 MmFreePagesByColor[ListHead->ListName][Color].Blink = (PVOID)Pfn1; 00321 } 00322 Pfn1->OriginalPte.u.Long = MM_EMPTY_LIST; 00323 00324 if (ListHead->ListName == ZeroedPageList) { 00325 MI_BARRIER_STAMP_ZEROED_PAGE (&Pfn1->PteFrame); 00326 } 00327 } 00328 else { 00329 00330 // 00331 // Transition page list so tally it appropriately. 00332 // 00333 00334 MI_TALLY_TRANSITION_PAGE_ADDITION (Pfn1); 00335 } 00336 00337 if ((ListHead->ListName == FreePageList) && 00338 (MmFreePageListHead.Total >= MmMinimumFreePagesToZero) && 00339 (MmZeroingPageThreadActive == FALSE)) { 00340 00341 // 00342 // There are enough pages on the free list, start 00343 // the zeroing page thread. 00344 // 00345 00346 MmZeroingPageThreadActive = TRUE; 00347 KeSetEvent (&MmZeroingPageEvent, 0, FALSE); 00348 } 00349 return; 00350 } 00351 00352 // 00353 // Check to see if there are too many modified pages. 00354 // 00355 00356 if (ListHead->ListName == ModifiedPageList) { 00357 00358 // 00359 // Transition page list so tally it appropriately. 00360 // 00361 00362 MI_TALLY_TRANSITION_PAGE_ADDITION (Pfn1); 00363 00364 if (Pfn1->OriginalPte.u.Soft.Prototype == 0) { 00365 ASSERT (Pfn1->OriginalPte.u.Soft.PageFileHigh == 0); 00366 } 00367 00368 PsGetCurrentProcess()->ModifiedPageCount += 1; 00369 if (MmModifiedPageListHead.Total >= MmModifiedPageMaximum ) { 00370 00371 // 00372 // Start the modified page writer. 00373 // 00374 00375 KeSetEvent (&MmModifiedPageWriterEvent, 0, FALSE); 00376 } 00377 } 00378 else if (ListHead->ListName == ModifiedNoWritePageList) { 00379 MI_TALLY_TRANSITION_PAGE_ADDITION (Pfn1); 00380 } 00381 00382 return; 00383 } 00384 00385 00386 VOID 00387 FASTCALL 00388 MiInsertStandbyListAtFront ( 00389 IN PFN_NUMBER PageFrameIndex 00390 ) 00391 00392 /*++ 00393 00394 Routine Description: 00395 00396 This procedure inserts a page at the front of the standby list. 00397 00398 Arguments: 00399 00400 PageFrameIndex - Supplies the physical page number to insert in the 00401 list. 00402 00403 Return Value: 00404 00405 none. 00406 00407 Environment: 00408 00409 Must be holding the PFN database mutex with APCs disabled. 00410 00411 --*/ 00412 00413 { 00414 PFN_NUMBER first; 00415 IN PMMPFNLIST ListHead; 00416 PMMPFN Pfn1; 00417 PMMPFN Pfn2; 00418 00419 MM_PFN_LOCK_ASSERT(); 00420 ASSERT ((PageFrameIndex != 0) && (PageFrameIndex <= MmHighestPhysicalPage) && 00421 (PageFrameIndex >= MmLowestPhysicalPage)); 00422 00423 // 00424 // Check to ensure the reference count for the page 00425 // is zero. 00426 // 00427 00428 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 00429 00430 PERFINFO_INSERT_FRONT_STANDBY(PageFrameIndex); 00431 00432 #if DBG 00433 if (MmDebug & MM_DBG_PAGE_REF_COUNT) { 00434 00435 PMMPTE PointerPte; 00436 KIRQL OldIrql = 99; 00437 00438 if ((Pfn1->u3.e1.PrototypePte == 1) && 00439 (MmIsAddressValid (Pfn1->PteAddress))) { 00440 PointerPte = Pfn1->PteAddress; 00441 } else { 00442 00443 // 00444 // The page containing the prototype PTE is not valid, 00445 // map the page into hyperspace and reference it that way. 00446 // 00447 00448 PointerPte = MiMapPageInHyperSpace (Pfn1->PteFrame, &OldIrql); 00449 PointerPte = (PMMPTE)((PCHAR)PointerPte + 00450 MiGetByteOffset(Pfn1->PteAddress)); 00451 } 00452 00453 ASSERT ((MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerPte) == PageFrameIndex) || 00454 (MI_GET_PAGE_FRAME_FROM_PTE (PointerPte) == PageFrameIndex)); 00455 ASSERT (PointerPte->u.Soft.Transition == 1); 00456 ASSERT (PointerPte->u.Soft.Prototype == 0); 00457 if (OldIrql != 99) { 00458 MiUnmapPageInHyperSpace (OldIrql) 00459 } 00460 } 00461 00462 if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) && 00463 (Pfn1->OriginalPte.u.Soft.Transition == 1)) { 00464 KeBugCheckEx (MEMORY_MANAGEMENT, 0x8889, 0,0,0); 00465 } 00466 #endif 00467 00468 ASSERT (Pfn1->u3.e2.ReferenceCount == 0); 00469 ASSERT (Pfn1->u3.e1.PrototypePte == 1); 00470 MmTransitionSharedPages += 1; 00471 00472 MmStandbyPageListHead.Total += 1; // One more page on the list. 00473 00474 ASSERT (MmTransitionPrivatePages + MmTransitionSharedPages == MmStandbyPageListHead.Total + MmModifiedPageListHead.Total + MmModifiedNoWritePageListHead.Total); 00475 00476 ListHead = &MmStandbyPageListHead; 00477 00478 first = ListHead->Flink; 00479 if (first == MM_EMPTY_LIST) { 00480 00481 // 00482 // List is empty add the page to the ListHead. 00483 // 00484 00485 ListHead->Blink = PageFrameIndex; 00486 } else { 00487 Pfn2 = MI_PFN_ELEMENT (first); 00488 Pfn2->u2.Blink = PageFrameIndex; 00489 } 00490 00491 ListHead->Flink = PageFrameIndex; 00492 Pfn1->u2.Blink = MM_EMPTY_LIST; 00493 Pfn1->u1.Flink = first; 00494 Pfn1->u3.e1.PageLocation = StandbyPageList; 00495 00496 // 00497 // If the page was placed on the free, standby or zeroed list, 00498 // update the count of usable pages in the system. If the count 00499 // transitions from 0 to 1, the event associated with available 00500 // pages should become true. 00501 // 00502 00503 MmAvailablePages += 1; 00504 00505 // 00506 // A page has just become available, check to see if the 00507 // page wait events should be signalled. 00508 // 00509 00510 if (MmAvailablePages == MM_LOW_LIMIT) { 00511 KeSetEvent (&MmAvailablePagesEvent, 0, FALSE); 00512 } else if (MmAvailablePages == MM_HIGH_LIMIT) { 00513 KeSetEvent (&MmAvailablePagesEventHigh, 0, FALSE); 00514 } 00515 00516 return; 00517 } 00518 00519 PFN_NUMBER //PageFrameIndex 00520 FASTCALL 00521 MiRemovePageFromList ( 00522 IN PMMPFNLIST ListHead 00523 ) 00524 00525 /*++ 00526 00527 Routine Description: 00528 00529 This procedure removes a page from the head of the specified list (free, 00530 standby, zeroed, modified). 00531 00532 This routine clears the flags word in the PFN database, hence the 00533 PFN information for this page must be initialized. 00534 00535 Arguments: 00536 00537 ListHead - Supplies the list of the list in which to remove the 00538 specified physical page. 00539 00540 Return Value: 00541 00542 The physical page number removed from the specified list. 00543 00544 Environment: 00545 00546 Must be holding the PFN database mutex with APCs disabled. 00547 00548 --*/ 00549 00550 { 00551 PFN_NUMBER PageFrameIndex; 00552 PMMPFN Pfn1; 00553 PMMPFN Pfn2; 00554 ULONG Color; 00555 00556 MM_PFN_LOCK_ASSERT(); 00557 00558 // 00559 // If the specified list is empty return MM_EMPTY_LIST. 00560 // 00561 00562 if (ListHead->Total == 0) { 00563 00564 KdPrint(("MM:Attempting to remove page from empty list\n")); 00565 KeBugCheckEx (PFN_LIST_CORRUPT, 1, (ULONG_PTR)ListHead, MmAvailablePages, 0); 00566 return 0; 00567 } 00568 00569 ASSERT (ListHead->ListName != ModifiedPageList); 00570 00571 // 00572 // Decrement the count of pages on the list and remove the first 00573 // page from the list. 00574 // 00575 00576 ListHead->Total -= 1; 00577 PageFrameIndex = ListHead->Flink; 00578 Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); 00579 00580 PERFINFO_REMOVEPAGE(PageFrameIndex, PERFINFO_LOG_TYPE_REMOVEPAGEFROMLIST); 00581 00582 ListHead->Flink = Pfn1->u1.Flink; 00583 00584 // 00585 // Zero the flink and blink in the pfn database element. 00586 // 00587 00588 Pfn1->u1.Flink = 0; // Assumes Flink width is >= WsIndex width 00589 Pfn1->u2.Blink = 0; 00590 00591 // 00592 // If the last page was removed (the ListHead->Flink is now 00593 // MM_EMPTY_LIST) make the listhead->Blink MM_EMPTY_LIST as well. 00594 // 00595 00596 if (ListHead->Flink == MM_EMPTY_LIST) { 00597 ListHead->Blink = MM_EMPTY_LIST; 00598 } else { 00599 00600 // 00601 // Make the PFN element point to MM_EMPTY_LIST signifying this 00602 // is the last page in the list. 00603 // 00604 00605 Pfn2 = MI_PFN_ELEMENT (ListHead->Flink); 00606 Pfn2->u2.Blink = MM_EMPTY_LIST; 00607 } 00608 00609 // 00610 // Check to see if we now have one less page available. 00611 // 00612 00613 if (ListHead->ListName <= StandbyPageList) { 00614 MmAvailablePages -= 1; 00615 00616 if (ListHead->ListName == StandbyPageList) { 00617 00618 // 00619 // This page is currently in transition, restore the PTE to 00620 // its original contents so this page can be reused. 00621 // 00622 00623 MI_TALLY_TRANSITION_PAGE_REMOVAL (Pfn1); 00624 MiRestoreTransitionPte (PageFrameIndex); 00625 } 00626 00627 if (MmAvailablePages < MmMinimumFreePages) { 00628 00629 // 00630 // Obtain free pages. 00631 // 00632 00633 MiObtainFreePages(); 00634 } 00635 } 00636 00637 ASSERT ((PageFrameIndex != 0) && 00638 (PageFrameIndex <= MmHighestPhysicalPage) && 00639 (PageFrameIndex >= MmLowestPhysicalPage)); 00640 00641 // 00642 // Zero the PFN flags longword. 00643 // 00644 00645 Color = Pfn1->u3.e1.PageColor; 00646 ASSERT (Pfn1->u3.e1.RemovalRequested == 0); 00647 Pfn1->u3.e2.ShortFlags = 0; 00648 Pfn1->u3.e1.PageColor = Color; 00649 Color = MI_GET_SECONDARY_COLOR (PageFrameIndex, Pfn1); 00650 00651 if (ListHead->ListName <= FreePageList) { 00652 00653 // 00654 // Update the color lists. 00655 // 00656 00657 ASSERT (MmFreePagesByColor[ListHead->ListName][Color].Flink == PageFrameIndex); 00658 MmFreePagesByColor[ListHead->ListName][Color].Flink = 00659 (PFN_NUMBER) Pfn1->OriginalPte.u.Long; 00660 } 00661 00662 return PageFrameIndex; 00663 } 00664 00665 VOID 00666 FASTCALL 00667 MiUnlinkPageFromList ( 00668 IN PMMPFN Pfn 00669 ) 00670 00671 /*++ 00672 00673 Routine Description: 00674 00675 This procedure removes a page from the middle of a list. This is 00676 designed for the faulting of transition pages from the standby and 00677 modified list and making them active and valid again. 00678 00679 Arguments: 00680 00681 Pfn - Supplies a pointer to the PFN database element for the physical 00682 page to remove from the list. 00683 00684 Return Value: 00685 00686 none. 00687 00688 Environment: 00689 00690 Must be holding the PFN database mutex with APCs disabled. 00691 00692 --*/ 00693 00694 { 00695 PMMPFNLIST ListHead; 00696 PFN_NUMBER Previous; 00697 PFN_NUMBER Next; 00698 PMMPFN Pfn2; 00699 00700 MM_PFN_LOCK_ASSERT(); 00701 00702 PERFINFO_UNLINKPAGE((ULONG_PTR)(Pfn - MmPfnDatabase), Pfn->u3.e1.PageLocation); 00703 00704 // 00705 // Page not on standby or modified list, check to see if the 00706 // page is currently being written by the modified page 00707 // writer, if so, just return this page. The reference 00708 // count for the page will be incremented, so when the modified 00709 // page write completes, the page will not be put back on 00710 // the list, rather, it will remain active and valid. 00711 // 00712 00713 if (Pfn->u3.e2.ReferenceCount > 0) { 00714 00715 // 00716 // The page was not on any "transition lists", check to see 00717 // if this has I/O in progress. 00718 // 00719 00720 if (Pfn->u2.ShareCount == 0) { 00721 #if DBG 00722 if (MmDebug & MM_DBG_PAGE_IN_LIST) { 00723 DbgPrint("unlinking page not in list...\n"); 00724 MiFormatPfn(Pfn); 00725 } 00726 #endif 00727 return; 00728 } 00729 KdPrint(("MM:attempt to remove page from wrong page list\n")); 00730 KeBugCheckEx (PFN_LIST_CORRUPT, 00731 2, 00732 Pfn - MmPfnDatabase, 00733 MmHighestPhysicalPage, 00734 Pfn->u3.e2.ReferenceCount); 00735 return; 00736 } 00737 00738 ListHead = MmPageLocationList[Pfn->u3.e1.PageLocation]; 00739 00740 // 00741 // Must not remove pages from free or zeroed without updating 00742 // the colored lists. 00743 // 00744 00745 ASSERT (ListHead->ListName >= StandbyPageList); 00746 00747 // 00748 // On MIPS R4000 modified pages destined for the paging file are 00749 // kept on separate lists which group pages of the same color 00750 // together 00751 // 00752 00753 if ((ListHead == &MmModifiedPageListHead) && 00754 (Pfn->OriginalPte.u.Soft.Prototype == 0)) { 00755 00756 // 00757 // This page is destined for the paging file (not 00758 // a mapped file). Change the list head to the 00759 // appropriate colored list head. 00760 // 00761 00762 ListHead->Total -= 1; 00763 MmTotalPagesForPagingFile -= 1; 00764 ListHead = &MmModifiedPageListByColor [Pfn->u3.e1.PageColor]; 00765 } 00766 00767 ASSERT (Pfn->u3.e1.WriteInProgress == 0); 00768 ASSERT (Pfn->u3.e1.ReadInProgress == 0); 00769 ASSERT (ListHead->Total != 0); 00770 00771 Next = Pfn->u1.Flink; 00772 Pfn->u1.Flink = 0; // Assumes Flink width is >= WsIndex width 00773 Previous = Pfn->u2.Blink; 00774 Pfn->u2.Blink = 0; 00775 00776 if (Next == MM_EMPTY_LIST) { 00777 ListHead->Blink = Previous; 00778 } else { 00779 Pfn2 = MI_PFN_ELEMENT(Next); 00780 Pfn2->u2.Blink = Previous; 00781 } 00782 00783 if (Previous == MM_EMPTY_LIST) { 00784 ListHead->Flink = Next; 00785 } else { 00786 Pfn2 = MI_PFN_ELEMENT(Previous); 00787 Pfn2->u1.Flink = Next; 00788 } 00789 00790 ListHead->Total -= 1; 00791 00792 // 00793 // Check to see if we now have one less page available. 00794 // 00795 00796 if (ListHead->ListName <= StandbyPageList) { 00797 MmAvailablePages -= 1; 00798 00799 if (ListHead->ListName == StandbyPageList) { 00800 MI_TALLY_TRANSITION_PAGE_REMOVAL (Pfn); 00801 } 00802 00803 if (MmAvailablePages < MmMinimumFreePages) { 00804 00805 // 00806 // Obtain free pages. 00807 // 00808 00809 MiObtainFreePages(); 00810 00811 } 00812 } 00813 else if (ListHead->ListName == ModifiedPageList || ListHead->ListName == ModifiedNoWritePageList) { 00814 MI_TALLY_TRANSITION_PAGE_REMOVAL (Pfn); 00815 } 00816 00817 return; 00818 } 00819 00820 VOID 00821 MiUnlinkFreeOrZeroedPage ( 00822 IN PFN_NUMBER Page 00823 ) 00824 00825 /*++ 00826 00827 Routine Description: 00828 00829 This procedure removes a page from the middle of a list. This is 00830 designed for the removing of free or zeroed pages from the middle of 00831 their lists. 00832 00833 Arguments: 00834 00835 Pfn - Supplies a pointer to the PFN database element for the physical 00836 page to remove from the list. 00837 00838 Return Value: 00839 00840 None. 00841 00842 Environment: 00843 00844 Must be holding the PFN database mutex with APCs disabled. 00845 00846 --*/ 00847 00848 { 00849 PMMPFNLIST ListHead; 00850 PFN_NUMBER Previous; 00851 PFN_NUMBER Next; 00852 PMMPFN Pfn2; 00853 PMMPFN Pfn; 00854 ULONG Color; 00855 00856 Pfn = MI_PFN_ELEMENT (Page); 00857 00858 MM_PFN_LOCK_ASSERT(); 00859 00860 ListHead = MmPageLocationList[Pfn->u3.e1.PageLocation]; 00861 ASSERT (ListHead->Total != 0); 00862 ListHead->Total -= 1; 00863 00864 ASSERT (ListHead->ListName <= FreePageList); 00865 ASSERT (Pfn->u3.e1.WriteInProgress == 0); 00866 ASSERT (Pfn->u3.e1.ReadInProgress == 0); 00867 00868 PERFINFO_UNLINKFREEPAGE((ULONG_PTR)(Pfn - MmPfnDatabase), Pfn->u3.e1.PageLocation); 00869 00870 Next = Pfn->u1.Flink; 00871 Pfn->u1.Flink = 0; // Assumes Flink width is >= WsIndex width 00872 Previous = Pfn->u2.Blink; 00873 Pfn->u2.Blink = 0; 00874 00875 if (Next == MM_EMPTY_LIST) { 00876 ListHead->Blink = Previous; 00877 } else { 00878 Pfn2 = MI_PFN_ELEMENT(Next); 00879 Pfn2->u2.Blink = Previous; 00880 } 00881 00882 if (Previous == MM_EMPTY_LIST) { 00883 ListHead->Flink = Next; 00884 } else { 00885 Pfn2 = MI_PFN_ELEMENT(Previous); 00886 Pfn2->u1.Flink = Next; 00887 } 00888 00889 // 00890 // We are removing a page from the middle of the free or zeroed page list. 00891 // The secondary color tables must be updated at this time. 00892 // 00893 00894 Color = MI_GET_SECONDARY_COLOR (Page, Pfn); 00895 ASSERT (Pfn->u3.e1.PageColor == MI_GET_COLOR_FROM_SECONDARY(Color)); 00896 00897 // 00898 // Walk down the list and remove the page. 00899 // 00900 00901 Next = MmFreePagesByColor[ListHead->ListName][Color].Flink; 00902 if (Next == Page) { 00903 MmFreePagesByColor[ListHead->ListName][Color].Flink = 00904 (PFN_NUMBER) Pfn->OriginalPte.u.Long; 00905 } else { 00906 00907 // 00908 // Walk the list to find the parent. 00909 // 00910 00911 for (; ; ) { 00912 Pfn2 = MI_PFN_ELEMENT (Next); 00913 Next = (PFN_NUMBER) Pfn2->OriginalPte.u.Long; 00914 if (Page == Next) { 00915 Pfn2->OriginalPte.u.Long = Pfn->OriginalPte.u.Long; 00916 if ((PFN_NUMBER) Pfn->OriginalPte.u.Long == MM_EMPTY_LIST) { 00917 MmFreePagesByColor[ListHead->ListName][Color].Blink = Pfn2; 00918 } 00919 break; 00920 } 00921 } 00922 } 00923 00924 MmAvailablePages -= 1; 00925 00926 if (MmAvailablePages < MmMinimumFreePages) { 00927 00928 // 00929 // Obtain free pages. 00930 // 00931 00932 MiObtainFreePages(); 00933 } 00934 00935 return; 00936 } 00937 00938 00939 ULONG 00940 FASTCALL 00941 MiEnsureAvailablePageOrWait ( 00942 IN PEPROCESS Process, 00943 IN PVOID VirtualAddress 00944 ) 00945 00946 /*++ 00947 00948 Routine Description: 00949 00950 This procedure ensures that a physical page is available on 00951 the zeroed, free or standby list such that the next call the remove a 00952 page absolutely will not block. This is necessary as blocking would 00953 require a wait which could cause a deadlock condition. 00954 00955 If a page is available the function returns immediately with a value 00956 of FALSE indicating no wait operation was performed. If no physical 00957 page is available, the thread enters a wait state and the function 00958 returns the value TRUE when the wait operation completes. 00959 00960 Arguments: 00961 00962 Process - Supplies a pointer to the current process if, and only if, 00963 the working set mutex is held currently held and should 00964 be released if a wait operation is issued. Supplies 00965 the value NULL otherwise. 00966 00967 VirtualAddress - Supplies the virtual address for the faulting page. 00968 If the value is NULL, the page is treated as a 00969 user mode address. 00970 00971 Return Value: 00972 00973 FALSE - if a page was immediately available. 00974 TRUE - if a wait operation occurred before a page became available. 00975 00976 00977 Environment: 00978 00979 Must be holding the PFN database mutex with APCs disabled. 00980 00981 --*/ 00982 00983 { 00984 PVOID Event; 00985 NTSTATUS Status; 00986 KIRQL OldIrql; 00987 KIRQL Ignore; 00988 ULONG Limit; 00989 ULONG Relock; 00990 PFN_NUMBER StrandedPages; 00991 LOGICAL WsHeldSafe; 00992 PMMPFN Pfn1; 00993 PMMPFN EndPfn; 00994 LARGE_INTEGER WaitBegin; 00995 LARGE_INTEGER WaitEnd; 00996 00997 MM_PFN_LOCK_ASSERT(); 00998 00999 if (MmAvailablePages >= MM_HIGH_LIMIT) { 01000 01001 // 01002 // Pages are available. 01003 // 01004 01005 return FALSE; 01006 } 01007 01008 // 01009 // If this fault is for paged pool (or pagable kernel space, 01010 // including page table pages), let it use the last page. 01011 // 01012 01013 #if defined(_IA64_) 01014 if (MI_IS_SYSTEM_ADDRESS(VirtualAddress) || 01015 (MI_IS_HYPER_SPACE_ADDRESS(VirtualAddress))) { 01016 #else 01017 if (((PMMPTE)VirtualAddress > MiGetPteAddress(HYPER_SPACE)) || 01018 ((VirtualAddress > MM_HIGHEST_USER_ADDRESS) && 01019 (VirtualAddress < (PVOID)PTE_BASE))) { 01020 #endif 01021 01022 // 01023 // This fault is in the system, use 1 page as the limit. 01024 // 01025 01026 if (MmAvailablePages >= MM_LOW_LIMIT) { 01027 01028 // 01029 // Pages are available. 01030 // 01031 01032 return FALSE; 01033 } 01034 01035 Limit = MM_LOW_LIMIT; 01036 Event = (PVOID)&MmAvailablePagesEvent; 01037 } else { 01038 Limit = MM_HIGH_LIMIT; 01039 Event = (PVOID)&MmAvailablePagesEventHigh; 01040 } 01041 01042 while (MmAvailablePages < Limit) { 01043 KeClearEvent ((PKEVENT)Event); 01044 01045 UNLOCK_PFN (APC_LEVEL); 01046 01047 if (Process == HYDRA_PROCESS) { 01048 UNLOCK_SESSION_SPACE_WS (APC_LEVEL); 01049 } 01050 else if (Process != NULL) { 01051 01052 // 01053 // The working set lock may have been acquired safely or unsafely 01054 // by our caller. Handle both cases here and below. 01055 // 01056 01057 UNLOCK_WS_REGARDLESS (Process, WsHeldSafe); 01058 } 01059 else { 01060 Relock = FALSE; 01061 if (MmSystemLockOwner == PsGetCurrentThread()) { 01062 UNLOCK_SYSTEM_WS (APC_LEVEL); 01063 Relock = TRUE; 01064 } 01065 } 01066 01067 KiQueryInterruptTime(&WaitBegin); 01068 01069 // 01070 // Wait 7 minutes for pages to become available. 01071 // 01072 01073 Status = KeWaitForSingleObject(Event, 01074 WrFreePage, 01075 KernelMode, 01076 FALSE, 01077 (PLARGE_INTEGER)&MmSevenMinutes); 01078 01079 if (Status == STATUS_TIMEOUT) { 01080 01081 KiQueryInterruptTime(&WaitEnd); 01082 01083 // 01084 // See how many transition pages have nonzero reference counts as 01085 // these indicate drivers that aren't unlocking the pages in their 01086 // MDLs. 01087 // 01088 01089 Limit = 0; 01090 StrandedPages = 0; 01091 01092 do { 01093 01094 Pfn1 = MI_PFN_ELEMENT (MmPhysicalMemoryBlock->Run[Limit].BasePage); 01095 EndPfn = Pfn1 + MmPhysicalMemoryBlock->Run[Limit].PageCount; 01096 01097 while (Pfn1 < EndPfn) { 01098 if ((Pfn1->u3.e1.PageLocation == TransitionPage) && 01099 (Pfn1->u3.e2.ReferenceCount != 0)) { 01100 StrandedPages += 1; 01101 } 01102 Pfn1 += 1; 01103 } 01104 Limit += 1; 01105 01106 } while (Limit != MmPhysicalMemoryBlock->NumberOfRuns); 01107 01108 // 01109 // This bugcheck can occur for the following reasons: 01110 // 01111 // A driver has blocked, deadlocking the modified or mapped 01112 // page writers. Examples of this include mutex deadlocks or 01113 // accesses to paged out memory in filesystem drivers, filter 01114 // drivers, etc. This indicates a driver bug. 01115 // 01116 // The storage driver(s) are not processing requests. Examples 01117 // of this are stranded queues, non-responding drives, etc. This 01118 // indicates a driver bug. 01119 // 01120 // Not enough pool is available for the storage stack to write out 01121 // modified pages. This indicates a driver bug. 01122 // 01123 // A high priority realtime thread has starved the balance set 01124 // manager from trimming pages and/or starved the modified writer 01125 // from writing them out. This indicates a bug in the component 01126 // that created this thread. 01127 // 01128 // All the processes have been trimmed to their minimums and all 01129 // modified pages written, but still no memory is available. The 01130 // freed memory must be stuck in transition pages with non-zero 01131 // reference counts - thus they cannot be put on the freelist. 01132 // A driver is neglecting to unlock the pages preventing the 01133 // reference counts from going to zero which would free the pages. 01134 // This may be due to transfers that never finish and the driver 01135 // never aborts or other driver bugs. 01136 // 01137 01138 KeBugCheckEx (NO_PAGES_AVAILABLE, 01139 MmModifiedPageListHead.Total, 01140 MmTotalPagesForPagingFile, 01141 (MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT) - MmAllocatedNonPagedPool, 01142 StrandedPages); 01143 01144 if (!KdDebuggerNotPresent) { 01145 DbgPrint ("MmEnsureAvailablePageOrWait: 7 min timeout %x %x %x %x\n", WaitEnd.HighPart, WaitEnd.LowPart, WaitBegin.HighPart, WaitBegin.LowPart); 01146 DbgBreakPoint (); 01147 } 01148 } 01149 01150 if (Process == HYDRA_PROCESS) { 01151 LOCK_SESSION_SPACE_WS (Ignore); 01152 } 01153 else if (Process != NULL) { 01154 01155 // 01156 // The working set lock may have been acquired safely or unsafely 01157 // by our caller. Reacquire it in the same manner our caller did. 01158 // 01159 01160 LOCK_WS_REGARDLESS (Process, WsHeldSafe); 01161 } 01162 else { 01163 if (Relock) { 01164 LOCK_SYSTEM_WS (Ignore); 01165 } 01166 } 01167 01168 LOCK_PFN (OldIrql); 01169 } 01170 01171 return TRUE; 01172 } 01173 01174 01175 PFN_NUMBER //PageFrameIndex 01176 FASTCALL 01177 MiRemoveZeroPage ( 01178 IN ULONG PageColor 01179 ) 01180 01181 /*++ 01182 01183 Routine Description: 01184 01185 This procedure removes a zero page from either the zeroed, free 01186 or standby lists (in that order). If no pages exist on the zeroed 01187 or free list a transition page is removed from the standby list 01188 and the PTE (may be a prototype PTE) which refers to this page is 01189 changed from transition back to its original contents. 01190 01191 If the page is not obtained from the zeroed list, it is zeroed. 01192 01193 Arguments: 01194 01195 PageColor - Supplies the page color for which this page is destined. 01196 This is used for checking virtual address alignments to 01197 determine if the D cache needs flushing before the page 01198 can be reused. 01199 01200 Return Value: 01201 01202 The physical page number removed from the specified list. 01203 01204 Environment: 01205 01206 Must be holding the PFN database mutex with APCs disabled. 01207 01208 --*/ 01209 01210 { 01211 PFN_NUMBER Page; 01212 PMMPFN Pfn1; 01213 ULONG Color; 01214 01215 MM_PFN_LOCK_ASSERT(); 01216 ASSERT(MmAvailablePages != 0); 01217 01218 // 01219 // Attempt to remove a page from the zeroed page list. If a page 01220 // is available, then remove it and return its page frame index. 01221 // Otherwise, attempt to remove a page from the free page list or 01222 // the standby list. 01223 // 01224 // N.B. It is not necessary to change page colors even if the old 01225 // color is not equal to the new color. The zero page thread 01226 // ensures that all zeroed pages are removed from all caches. 01227 // 01228 01229 if (MmFreePagesByColor[ZeroedPageList][PageColor].Flink != MM_EMPTY_LIST) { 01230 01231 // 01232 // Remove the first entry on the zeroed by color list. 01233 // 01234 01235 Page = MmFreePagesByColor[ZeroedPageList][PageColor].Flink; 01236 01237 #if DBG 01238 Pfn1 = MI_PFN_ELEMENT(Page); 01239 ASSERT (Pfn1->u3.e1.PageLocation == ZeroedPageList); 01240 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01241 #endif 01242 01243 MiRemovePageByColor (Page, PageColor); 01244 01245 #if DBG 01246 ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK)); 01247 ASSERT (Pfn1->u3.e2.ReferenceCount == 0); 01248 ASSERT (Pfn1->u2.ShareCount == 0); 01249 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01250 #endif 01251 return Page; 01252 01253 } 01254 01255 // 01256 // No previously zeroed page with the specified secondary color exists. 01257 // Try a zeroed page of the primary color. 01258 // 01259 01260 if (MmZeroedPageListHead.Flink != MM_EMPTY_LIST) { 01261 Page = MmZeroedPageListHead.Flink; 01262 #if DBG 01263 Pfn1 = MI_PFN_ELEMENT(Page); 01264 ASSERT (Pfn1->u3.e1.PageLocation == ZeroedPageList); 01265 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01266 #endif 01267 Color = MI_GET_SECONDARY_COLOR (Page, MI_PFN_ELEMENT(Page)); 01268 MiRemovePageByColor (Page, Color); 01269 #if DBG 01270 Pfn1 = MI_PFN_ELEMENT(Page); 01271 ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK)); 01272 ASSERT (Pfn1->u3.e2.ReferenceCount == 0); 01273 ASSERT (Pfn1->u2.ShareCount == 0); 01274 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01275 #endif 01276 return Page; 01277 } 01278 01279 // 01280 // No zeroed page of the primary color exists, try a free page of the 01281 // secondary color. 01282 // 01283 01284 if (MmFreePagesByColor[FreePageList][PageColor].Flink != MM_EMPTY_LIST) { 01285 01286 // 01287 // Remove the first entry on the free list by color. 01288 // 01289 01290 Page = MmFreePagesByColor[FreePageList][PageColor].Flink; 01291 01292 #if DBG 01293 Pfn1 = MI_PFN_ELEMENT(Page); 01294 ASSERT (Pfn1->u3.e1.PageLocation == FreePageList); 01295 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01296 #endif 01297 01298 MiRemovePageByColor (Page, PageColor); 01299 #if DBG 01300 Pfn1 = MI_PFN_ELEMENT(Page); 01301 ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK)); 01302 ASSERT (Pfn1->u3.e2.ReferenceCount == 0); 01303 ASSERT (Pfn1->u2.ShareCount == 0); 01304 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01305 #endif 01306 goto ZeroPage; 01307 } 01308 01309 if (MmFreePageListHead.Flink != MM_EMPTY_LIST) { 01310 Page = MmFreePageListHead.Flink; 01311 01312 Color = MI_GET_SECONDARY_COLOR (Page, MI_PFN_ELEMENT(Page)); 01313 MiRemovePageByColor (Page, Color); 01314 #if DBG 01315 Pfn1 = MI_PFN_ELEMENT(Page); 01316 ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK)); 01317 ASSERT (Pfn1->u3.e2.ReferenceCount == 0); 01318 ASSERT (Pfn1->u2.ShareCount == 0); 01319 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01320 #endif 01321 goto ZeroPage; 01322 } 01323 01324 ASSERT (MmZeroedPageListHead.Total == 0); 01325 ASSERT (MmFreePageListHead.Total == 0); 01326 01327 if (MmZeroedPageListHead.Total != 0) { 01328 01329 Page = MiRemovePageFromList(&MmZeroedPageListHead); 01330 MI_CHECK_PAGE_ALIGNMENT(Page, PageColor & MM_COLOR_MASK); 01331 01332 } else { 01333 01334 // 01335 // Attempt to remove a page from the free list. If a page is 01336 // available, then remove it. Otherwise, attempt to remove a 01337 // page from the standby list. 01338 // 01339 01340 if (MmFreePageListHead.Total != 0) { 01341 Page = MiRemovePageFromList(&MmFreePageListHead); 01342 ASSERT ((MI_PFN_ELEMENT(Page))->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01343 } else { 01344 01345 // 01346 // Remove a page from the standby list and restore the original 01347 // contents of the PTE to free the last reference to the physical 01348 // page. 01349 // 01350 01351 ASSERT (MmStandbyPageListHead.Total != 0); 01352 01353 Page = MiRemovePageFromList(&MmStandbyPageListHead); 01354 ASSERT ((MI_PFN_ELEMENT(Page))->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01355 } 01356 01357 // 01358 // Zero the page removed from the free or standby list. 01359 // 01360 01361 ZeroPage: 01362 01363 Pfn1 = MI_PFN_ELEMENT(Page); 01364 01365 #if defined(_ALPHA_) 01366 HalZeroPage((PVOID)ULongToPtr((PageColor & MM_COLOR_MASK) << PAGE_SHIFT), 01367 (PVOID)ULongToPtr((Pfn1->u3.e1.PageColor) << PAGE_SHIFT), 01368 Page); 01369 #else 01370 MiZeroPhysicalPage (Page, 0); 01371 #endif 01372 01373 // 01374 // Note the stamping must occur after the page is zeroed. 01375 // 01376 01377 MI_BARRIER_STAMP_ZEROED_PAGE (&Pfn1->PteFrame); 01378 01379 Pfn1->u3.e1.PageColor = PageColor & MM_COLOR_MASK; 01380 01381 } 01382 01383 #if DBG 01384 Pfn1 = MI_PFN_ELEMENT (Page); 01385 ASSERT (Pfn1->u3.e2.ReferenceCount == 0); 01386 ASSERT (Pfn1->u2.ShareCount == 0); 01387 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01388 #endif 01389 01390 return Page; 01391 } 01392 01393 PFN_NUMBER //PageFrameIndex 01394 FASTCALL 01395 MiRemoveAnyPage ( 01396 IN ULONG PageColor 01397 ) 01398 01399 /*++ 01400 01401 Routine Description: 01402 01403 This procedure removes a page from either the free, zeroed, 01404 or standby lists (in that order). If no pages exist on the zeroed 01405 or free list a transition page is removed from the standby list 01406 and the PTE (may be a prototype PTE) which refers to this page is 01407 changed from transition back to its original contents. 01408 01409 Note pages MUST exist to satisfy this request. The caller ensures this 01410 by first calling MiEnsureAvailablePageOrWait. 01411 01412 Arguments: 01413 01414 PageColor - Supplies the page color for which this page is destined. 01415 This is used for checking virtual address alignments to 01416 determine if the D cache needs flushing before the page 01417 can be reused. 01418 01419 Return Value: 01420 01421 The physical page number removed from the specified list. 01422 01423 Environment: 01424 01425 Must be holding the PFN database mutex with APCs disabled. 01426 01427 --*/ 01428 01429 { 01430 PFN_NUMBER Page; 01431 PMMPFN Pfn1; 01432 ULONG Color; 01433 01434 MM_PFN_LOCK_ASSERT(); 01435 ASSERT(MmAvailablePages != 0); 01436 01437 // 01438 // Check the free page list, and if a page is available 01439 // remove it and return its value. 01440 // 01441 01442 if (MmFreePagesByColor[FreePageList][PageColor].Flink != MM_EMPTY_LIST) { 01443 01444 // 01445 // Remove the first entry on the free by color list. 01446 // 01447 01448 Page = MmFreePagesByColor[FreePageList][PageColor].Flink; 01449 #if DBG 01450 Pfn1 = MI_PFN_ELEMENT(Page); 01451 ASSERT (Pfn1->u3.e1.PageLocation == FreePageList); 01452 ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK)); 01453 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01454 #endif 01455 MiRemovePageByColor (Page, PageColor); 01456 #if DBG 01457 ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK)); 01458 ASSERT (Pfn1->u3.e2.ReferenceCount == 0); 01459 ASSERT (Pfn1->u2.ShareCount == 0); 01460 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01461 #endif 01462 return Page; 01463 01464 } 01465 01466 if (MmFreePagesByColor[ZeroedPageList][PageColor].Flink 01467 != MM_EMPTY_LIST) { 01468 01469 // 01470 // Remove the first entry on the zeroed by color list. 01471 // 01472 01473 Page = MmFreePagesByColor[ZeroedPageList][PageColor].Flink; 01474 #if DBG 01475 Pfn1 = MI_PFN_ELEMENT(Page); 01476 ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK)); 01477 ASSERT (Pfn1->u3.e1.PageLocation == ZeroedPageList); 01478 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01479 #endif 01480 01481 MiRemovePageByColor (Page, PageColor); 01482 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01483 return Page; 01484 } 01485 01486 // 01487 // Try the free page list by primary color. 01488 // 01489 01490 if (MmFreePageListHead.Flink != MM_EMPTY_LIST) { 01491 Page = MmFreePageListHead.Flink; 01492 Color = MI_GET_SECONDARY_COLOR (Page, MI_PFN_ELEMENT(Page)); 01493 01494 #if DBG 01495 Pfn1 = MI_PFN_ELEMENT(Page); 01496 ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK)); 01497 ASSERT (Pfn1->u3.e1.PageLocation == FreePageList); 01498 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01499 #endif 01500 MiRemovePageByColor (Page, Color); 01501 #if DBG 01502 ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK)); 01503 ASSERT (Pfn1->u3.e2.ReferenceCount == 0); 01504 ASSERT (Pfn1->u2.ShareCount == 0); 01505 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01506 #endif 01507 return Page; 01508 01509 } 01510 01511 if (MmZeroedPageListHead.Flink != MM_EMPTY_LIST) { 01512 Page = MmZeroedPageListHead.Flink; 01513 Color = MI_GET_SECONDARY_COLOR (Page, MI_PFN_ELEMENT(Page)); 01514 MiRemovePageByColor (Page, Color); 01515 #if DBG 01516 Pfn1 = MI_PFN_ELEMENT(Page); 01517 ASSERT (Pfn1->u3.e1.PageColor == (PageColor & MM_COLOR_MASK)); 01518 ASSERT (Pfn1->u3.e2.ReferenceCount == 0); 01519 ASSERT (Pfn1->u2.ShareCount == 0); 01520 ASSERT (Pfn1->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01521 #endif 01522 return Page; 01523 } 01524 01525 if (MmFreePageListHead.Total != 0) { 01526 01527 Page = MiRemovePageFromList(&MmFreePageListHead); 01528 ASSERT ((MI_PFN_ELEMENT(Page))->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01529 01530 } else { 01531 01532 // 01533 // Check the zeroed page list, and if a page is available 01534 // remove it and return its value. 01535 // 01536 01537 if (MmZeroedPageListHead.Total != 0) { 01538 01539 Page = MiRemovePageFromList(&MmZeroedPageListHead); 01540 ASSERT ((MI_PFN_ELEMENT(Page))->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01541 01542 } else { 01543 01544 // 01545 // No pages exist on the free or zeroed list, use the 01546 // standby list. 01547 // 01548 01549 ASSERT(MmStandbyPageListHead.Total != 0); 01550 01551 Page = MiRemovePageFromList(&MmStandbyPageListHead); 01552 ASSERT ((MI_PFN_ELEMENT(Page))->PteFrame != MI_MAGIC_AWE_PTEFRAME); 01553 } 01554 } 01555 01556 MI_CHECK_PAGE_ALIGNMENT(Page, PageColor & MM_COLOR_MASK); 01557 #if DBG 01558 Pfn1 = MI_PFN_ELEMENT (Page); 01559 ASSERT (Pfn1->u3.e2.ReferenceCount == 0); 01560 ASSERT (Pfn1->u2.ShareCount == 0); 01561 #endif 01562 return Page; 01563 } 01564 01565 01566 VOID 01567 MiRemovePageByColor ( 01568 IN PFN_NUMBER Page, 01569 IN ULONG Color 01570 ) 01571 01572 /*++ 01573 01574 Routine Description: 01575 01576 This procedure removes a page from the middle of the free or 01577 zeroed page list. 01578 01579 Arguments: 01580 01581 PageFrameIndex - Supplies the physical page number to unlink from the 01582 list. 01583 01584 Return Value: 01585 01586 none. 01587 01588 Environment: 01589 01590 Must be holding the PFN database mutex with APCs disabled. 01591 01592 --*/ 01593 01594 { 01595 PMMPFNLIST ListHead; 01596 PMMPFNLIST PrimaryListHead; 01597 PFN_NUMBER Previous; 01598 PFN_NUMBER Next; 01599 PMMPFN Pfn1; 01600 PMMPFN Pfn2; 01601 ULONG PrimaryColor; 01602 01603 MM_PFN_LOCK_ASSERT(); 01604 01605 Pfn1 = MI_PFN_ELEMENT (Page); 01606 PrimaryColor = Pfn1->u3.e1.PageColor; 01607 01608 PERFINFO_REMOVEPAGE(Page, PERFINFO_LOG_TYPE_REMOVEPAGEBYCOLOR); 01609 01610 ListHead = MmPageLocationList[Pfn1->u3.e1.PageLocation]; 01611 01612 ListHead->Total -= 1; 01613 01614 PrimaryListHead = ListHead; 01615 01616 #if PFN_CONSISTENCY 01617 if (MmFreePagesByColor[PrimaryListHead->ListName][Color].Flink != Page) { 01618 01619 KeBugCheckEx (PFN_LIST_CORRUPT, 01620 0x9A, 01621 Page, 01622 Color, 01623 (PFN_NUMBER)&MmFreePagesByColor[PrimaryListHead->ListName][Color].Flink); 01624 } 01625 #endif 01626 01627 Next = Pfn1->u1.Flink; 01628 Pfn1->u1.Flink = 0; // Assumes Flink width is >= WsIndex width 01629 Previous = Pfn1->u2.Blink; 01630 Pfn1->u2.Blink = 0; 01631 01632 if (Next == MM_EMPTY_LIST) { 01633 PrimaryListHead->Blink = Previous; 01634 } else { 01635 Pfn2 = MI_PFN_ELEMENT(Next); 01636 Pfn2->u2.Blink = Previous; 01637 } 01638 01639 if (Previous == MM_EMPTY_LIST) { 01640 PrimaryListHead->Flink = Next; 01641 } else { 01642 Pfn2 = MI_PFN_ELEMENT(Previous); 01643 Pfn2->u1.Flink = Next; 01644 } 01645 01646 // 01647 // Zero the flags longword, but keep the color information. 01648 // 01649 01650 ASSERT (Pfn1->u3.e1.RemovalRequested == 0); 01651 Pfn1->u3.e2.ShortFlags = 0; 01652 Pfn1->u3.e1.PageColor = PrimaryColor; 01653 01654 // 01655 // Update the color lists. 01656 // 01657 01658 MmFreePagesByColor[ListHead->ListName][Color].Flink = 01659 (PFN_NUMBER) Pfn1->OriginalPte.u.Long; 01660 01661 // 01662 // Note that we now have one less page available. 01663 // 01664 01665 MmAvailablePages -= 1; 01666 01667 if (MmAvailablePages < MmMinimumFreePages) { 01668 01669 // 01670 // Obtain free pages. 01671 // 01672 01673 MiObtainFreePages(); 01674 01675 } 01676 01677 return; 01678 } 01679 01680 01681 VOID 01682 FASTCALL 01683 MiInsertFrontModifiedNoWrite ( 01684 IN PFN_NUMBER PageFrameIndex 01685 ) 01686 01687 /*++ 01688 01689 Routine Description: 01690 01691 This procedure inserts a page at the FRONT of the modified no 01692 write list. 01693 01694 Arguments: 01695 01696 PageFrameIndex - Supplies the physical page number to insert in the 01697 list. 01698 01699 Return Value: 01700 01701 none. 01702 01703 Environment: 01704 01705 Must be holding the PFN database mutex with APCs disabled. 01706 01707 --*/ 01708 01709 { 01710 PFN_NUMBER first; 01711 PMMPFN Pfn1; 01712 PMMPFN Pfn2; 01713 01714 MM_PFN_LOCK_ASSERT(); 01715 ASSERT ((PageFrameIndex != 0) && (PageFrameIndex <= MmHighestPhysicalPage) && 01716 (PageFrameIndex >= MmLowestPhysicalPage)); 01717 01718 // 01719 // Check to ensure the reference count for the page 01720 // is zero. 01721 // 01722 01723 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex); 01724 01725 ASSERT (Pfn1->u3.e2.ReferenceCount == 0); 01726 01727 MmModifiedNoWritePageListHead.Total += 1; // One more page on the list. 01728 01729 MI_TALLY_TRANSITION_PAGE_ADDITION (Pfn1); 01730 01731 first = MmModifiedNoWritePageListHead.Flink; 01732 if (first == MM_EMPTY_LIST) { 01733 01734 // 01735 // List is empty add the page to the ListHead. 01736 // 01737 01738 MmModifiedNoWritePageListHead.Blink = PageFrameIndex; 01739 } else { 01740 Pfn2 = MI_PFN_ELEMENT (first); 01741 Pfn2->u2.Blink = PageFrameIndex; 01742 } 01743 01744 MmModifiedNoWritePageListHead.Flink = PageFrameIndex; 01745 Pfn1->u1.Flink = first; 01746 Pfn1->u2.Blink = MM_EMPTY_LIST; 01747 Pfn1->u3.e1.PageLocation = ModifiedNoWritePageList; 01748 return; 01749 }

Generated on Sat May 15 19:41:09 2004 for test by doxygen 1.3.7