Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2758 lines
72 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. pfnlist.c
  5. Abstract:
  6. This module contains the routines to manipulate pages within the
  7. Page Frame Database.
  8. Author:
  9. Lou Perazzoli (loup) 4-Apr-1989
  10. Landy Wang (landyw) 02-June-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. //
  15. // The following line will generate an error is the number of colored
  16. // free page lists is not 2, ie ZeroedPageList and FreePageList. If
  17. // this number is changed, the size of the FreeCount array in the kernel
  18. // node structure (KNODE) must be updated.
  19. //
  20. C_ASSERT(StandbyPageList == 2);
  21. #define MM_LOW_LIMIT 2
  22. KEVENT MmAvailablePagesEventHigh;
  23. KEVENT MmAvailablePagesEventMedium;
  24. PFN_NUMBER MmTransitionPrivatePages;
  25. PFN_NUMBER MmTransitionSharedPages;
  26. #define MI_TALLY_TRANSITION_PAGE_ADDITION(Pfn) \
  27. if (Pfn->u3.e1.PrototypePte) { \
  28. MmTransitionSharedPages += 1; \
  29. } \
  30. else { \
  31. MmTransitionPrivatePages += 1; \
  32. }
  33. #if 0
  34. ASSERT (MmTransitionPrivatePages + MmTransitionSharedPages == MmStandbyPageListHead.Total + MmModifiedPageListHead.Total + MmModifiedNoWritePageListHead.Total);
  35. #endif
  36. #define MI_TALLY_TRANSITION_PAGE_REMOVAL(Pfn) \
  37. if (Pfn->u3.e1.PrototypePte) { \
  38. MmTransitionSharedPages -= 1; \
  39. } \
  40. else { \
  41. MmTransitionPrivatePages -= 1; \
  42. }
  43. #if 0
  44. ASSERT (MmTransitionPrivatePages + MmTransitionSharedPages == MmStandbyPageListHead.Total + MmModifiedPageListHead.Total + MmModifiedNoWritePageListHead.Total);
  45. #endif
  46. //
  47. // This counter is used to determine if standby pages are being cannibalized
  48. // for use as free pages and therefore more aging should be attempted.
  49. //
  50. ULONG MiStandbyRemoved;
  51. #if DBG
  52. ULONG MiSaveStacks;
  53. #endif
  54. extern ULONG MmSystemShutdown;
  55. PFN_NUMBER
  56. FASTCALL
  57. MiRemovePageByColor (
  58. IN PFN_NUMBER Page,
  59. IN ULONG PageColor
  60. );
  61. VOID
  62. FASTCALL
  63. MiLogPfnInformation (
  64. IN PMMPFN Pfn1,
  65. IN USHORT Reason
  66. );
  67. extern LOGICAL MiZeroingDisabled;
  68. // #define _MI_DEBUG_PFN 1
  69. #if defined (_MI_DEBUG_PFN)
  70. #define MI_PFN_TRACE_MAX 0x8000
  71. #define MI_PFN_BACKTRACE_LENGTH 6
  72. typedef struct _MI_PFN_TRACES {
  73. PFN_NUMBER PageFrameIndex;
  74. MMLISTS Destination;
  75. MMPTE PteContents;
  76. PVOID Thread;
  77. MMPFN Pfn;
  78. PVOID StackTrace [MI_PFN_BACKTRACE_LENGTH];
  79. } MI_PFN_TRACES, *PMI_PFN_TRACES;
  80. LONG MiPfnIndex;
  81. PMI_PFN_TRACES MiPfnTraces;
  82. ULONG zpfn = 0x1;
  83. VOID
  84. FORCEINLINE
  85. MiSnapPfn (
  86. IN PMMPFN Pfn1,
  87. IN MMLISTS Destination,
  88. IN ULONG CallerId
  89. )
  90. {
  91. MMPTE PteContents;
  92. PMMPTE PointerPte;
  93. PMI_PFN_TRACES Information;
  94. ULONG Hash;
  95. ULONG Index;
  96. PEPROCESS Process;
  97. if (MiPfnTraces == NULL) {
  98. return;
  99. }
  100. if (zpfn & 0x1) {
  101. if (Pfn1->PteAddress < MiGetPteAddress (MmPagedPoolStart)) {
  102. return;
  103. }
  104. if (Pfn1->PteAddress > MiGetPteAddress (MmPagedPoolEnd)) {
  105. return;
  106. }
  107. }
  108. if (MmIsAddressValid (Pfn1->PteAddress)) {
  109. PointerPte = Pfn1->PteAddress;
  110. PointerPte = (PMMPTE)((ULONG_PTR)PointerPte & ~0x1);
  111. PteContents = *PointerPte;
  112. }
  113. else {
  114. //
  115. // The containing page table page is not valid,
  116. // map the page into hyperspace and reference it that way.
  117. //
  118. Process = PsGetCurrentProcess ();
  119. PointerPte = MiMapPageInHyperSpaceAtDpc (Process, Pfn1->u4.PteFrame);
  120. PointerPte = (PMMPTE)((PCHAR)PointerPte +
  121. MiGetByteOffset(Pfn1->PteAddress));
  122. PointerPte = (PMMPTE)((ULONG_PTR)PointerPte & ~0x1);
  123. PteContents = *PointerPte;
  124. MiUnmapPageInHyperSpaceFromDpc (Process, PointerPte);
  125. }
  126. Index = InterlockedIncrement(&MiPfnIndex);
  127. Index &= (MI_PFN_TRACE_MAX - 1);
  128. Information = &MiPfnTraces[Index];
  129. Information->PageFrameIndex = (Pfn1 - MmPfnDatabase);
  130. Information->Destination = Destination;
  131. Information->PteContents = PteContents;
  132. Information->Thread = (PVOID)((ULONG_PTR)KeGetCurrentThread() | (CallerId));
  133. Information->Pfn = *Pfn1;
  134. RtlCaptureStackBackTrace (0, MI_PFN_BACKTRACE_LENGTH, Information->StackTrace, &Hash);
  135. }
  136. #define MI_SNAP_PFN(_Pfn, dest, callerid) MiSnapPfn(_Pfn, dest, callerid)
  137. #else
  138. #define MI_SNAP_PFN(_Pfn, dest, callerid)
  139. #endif
  140. VOID
  141. MiInitializePfnTracing (
  142. VOID
  143. )
  144. {
  145. #if defined (_MI_DEBUG_PFN)
  146. MiPfnTraces = ExAllocatePoolWithTag (NonPagedPool,
  147. MI_PFN_TRACE_MAX * sizeof (MI_PFN_TRACES),
  148. 'tCmM');
  149. #endif
  150. }
  151. VOID
  152. FASTCALL
  153. MiInsertPageInFreeList (
  154. IN PFN_NUMBER PageFrameIndex
  155. )
  156. /*++
  157. Routine Description:
  158. This procedure inserts a page at the end of the free list.
  159. Arguments:
  160. PageFrameIndex - Supplies the physical page number to insert in the list.
  161. Return Value:
  162. None.
  163. Environment:
  164. PFN lock held.
  165. --*/
  166. {
  167. PFN_NUMBER last;
  168. PMMPFN Pfn1;
  169. PMMPFN Pfn2;
  170. ULONG Color;
  171. MMLISTS ListName;
  172. PMMPFNLIST ListHead;
  173. PMMCOLOR_TABLES ColorHead;
  174. MM_PFN_LOCK_ASSERT();
  175. ASSERT ((PageFrameIndex != 0) &&
  176. (PageFrameIndex <= MmHighestPhysicalPage) &&
  177. (PageFrameIndex >= MmLowestPhysicalPage));
  178. Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
  179. MI_SNAP_DATA (Pfn1, Pfn1->PteAddress, 8);
  180. MI_SNAP_PFN(Pfn1, FreePageList, 0x1);
  181. if (PERFINFO_IS_GROUP_ON(PERF_MEMORY)) {
  182. MiLogPfnInformation (Pfn1, PERFINFO_LOG_TYPE_INSERTINFREELIST);
  183. }
  184. if (Pfn1->u3.e1.Rom == 1) {
  185. //
  186. // ROM pages do not go on free lists and are not counted towards
  187. // MmAvailablePages as they are not reusable. Transform these pages
  188. // into their pre-Phase 0 state and keep them off all lists.
  189. //
  190. ASSERT (XIPConfigured == TRUE);
  191. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  192. ASSERT (Pfn1->u3.e1.Modified == 0);
  193. ASSERT (Pfn1->u2.ShareCount == 0);
  194. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  195. ASSERT (Pfn1->u4.InPageError == 0);
  196. ASSERT (Pfn1->u3.e1.PrototypePte == 1);
  197. Pfn1->u1.Flink = 0;
  198. Pfn1->u3.e1.PageLocation = 0;
  199. return;
  200. }
  201. if (Pfn1->u3.e1.RemovalRequested == 1) {
  202. Pfn1->u3.e1.CacheAttribute = MiNotMapped;
  203. MiInsertPageInList (&MmBadPageListHead, PageFrameIndex);
  204. return;
  205. }
  206. ListHead = &MmFreePageListHead;
  207. ASSERT (Pfn1->u3.e1.LockCharged == 0);
  208. PERFINFO_INSERTINLIST(PageFrameIndex, ListHead);
  209. ListName = FreePageList;
  210. #if DBG
  211. #if defined (_X86_)
  212. if ((MiSaveStacks != 0) && (MmFirstReservedMappingPte != NULL)) {
  213. ULONG_PTR StackPointer;
  214. ULONG_PTR StackBytes;
  215. PULONG_PTR DataPage;
  216. PEPROCESS Process;
  217. _asm {
  218. mov StackPointer, esp
  219. }
  220. MiZeroingDisabled = TRUE;
  221. Process = PsGetCurrentProcess ();
  222. DataPage = MiMapPageInHyperSpaceAtDpc (Process, PageFrameIndex);
  223. DataPage[0] = StackPointer;
  224. //
  225. // For now, don't get fancy with copying more than what's in the current
  226. // stack page. To do so would require checking the thread stack limits,
  227. // DPC stack limits, etc.
  228. //
  229. StackBytes = PAGE_SIZE - BYTE_OFFSET(StackPointer);
  230. DataPage[1] = StackBytes;
  231. if (StackBytes != 0) {
  232. if (StackBytes > MI_STACK_BYTES) {
  233. StackBytes = MI_STACK_BYTES;
  234. }
  235. RtlCopyMemory ((PVOID)&DataPage[2], (PVOID)StackPointer, StackBytes);
  236. }
  237. MiUnmapPageInHyperSpaceFromDpc (Process, DataPage);
  238. }
  239. #endif
  240. #endif
  241. ASSERT (Pfn1->u4.VerifierAllocation == 0);
  242. //
  243. // Check to ensure the reference count for the page is zero.
  244. //
  245. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  246. ListHead->Total += 1; // One more page on the list.
  247. last = ListHead->Blink;
  248. if (last != MM_EMPTY_LIST) {
  249. Pfn2 = MI_PFN_ELEMENT (last);
  250. Pfn2->u1.Flink = PageFrameIndex;
  251. }
  252. else {
  253. //
  254. // List is empty, add the page to the ListHead.
  255. //
  256. ListHead->Flink = PageFrameIndex;
  257. }
  258. ListHead->Blink = PageFrameIndex;
  259. Pfn1->u1.Flink = MM_EMPTY_LIST;
  260. Pfn1->u2.Blink = last;
  261. Pfn1->u3.e1.PageLocation = ListName;
  262. Pfn1->u3.e1.CacheAttribute = MiNotMapped;
  263. Pfn1->u4.InPageError = 0;
  264. //
  265. // Update the count of usable pages in the system. If the count
  266. // transitions from 0 to 1, the event associated with available
  267. // pages should become true.
  268. //
  269. MmAvailablePages += 1;
  270. //
  271. // A page has just become available, check to see if the
  272. // page wait events should be signaled.
  273. //
  274. if (MmAvailablePages <= MM_HIGH_LIMIT) {
  275. if (MmAvailablePages == MM_HIGH_LIMIT) {
  276. KeSetEvent (&MmAvailablePagesEventHigh, 0, FALSE);
  277. }
  278. else if (MmAvailablePages == MM_MEDIUM_LIMIT) {
  279. KeSetEvent (&MmAvailablePagesEventMedium, 0, FALSE);
  280. }
  281. else if (MmAvailablePages == MM_LOW_LIMIT) {
  282. KeSetEvent (&MmAvailablePagesEvent, 0, FALSE);
  283. }
  284. }
  285. //
  286. // Signal applications if the freed page crosses a threshold.
  287. //
  288. if (MmAvailablePages == MmLowMemoryThreshold) {
  289. KeClearEvent (MiLowMemoryEvent);
  290. }
  291. else if (MmAvailablePages == MmHighMemoryThreshold) {
  292. KeSetEvent (MiHighMemoryEvent, 0, FALSE);
  293. }
  294. #if defined(MI_MULTINODE)
  295. //
  296. // Increment the free page count for this node.
  297. //
  298. if (KeNumberNodes > 1) {
  299. KeNodeBlock[Pfn1->u3.e1.PageColor]->FreeCount[ListName]++;
  300. }
  301. #endif
  302. //
  303. // We are adding a page to the free page list.
  304. // Add the page to the end of the correct colored page list.
  305. //
  306. Color = MI_GET_COLOR_FROM_LIST_ENTRY(PageFrameIndex, Pfn1);
  307. ColorHead = &MmFreePagesByColor[ListName][Color];
  308. if (ColorHead->Flink == MM_EMPTY_LIST) {
  309. //
  310. // This list is empty, add this as the first and last
  311. // entry.
  312. //
  313. ColorHead->Flink = PageFrameIndex;
  314. ColorHead->Blink = Pfn1;
  315. }
  316. else {
  317. Pfn2 = (PMMPFN)ColorHead->Blink;
  318. Pfn2->OriginalPte.u.Long = PageFrameIndex;
  319. ColorHead->Blink = Pfn1;
  320. }
  321. ColorHead->Count += 1;
  322. Pfn1->OriginalPte.u.Long = MM_EMPTY_LIST;
  323. if ((ListHead->Total >= MmMinimumFreePagesToZero) &&
  324. (MmZeroingPageThreadActive == FALSE)) {
  325. //
  326. // There are enough pages on the free list, start
  327. // the zeroing page thread.
  328. //
  329. MmZeroingPageThreadActive = TRUE;
  330. KeSetEvent (&MmZeroingPageEvent, 0, FALSE);
  331. }
  332. return;
  333. }
  334. VOID
  335. FASTCALL
  336. MiInsertPageInList (
  337. IN PMMPFNLIST ListHead,
  338. IN PFN_NUMBER PageFrameIndex
  339. )
  340. /*++
  341. Routine Description:
  342. This procedure inserts a page at the end of the specified list (standby,
  343. bad, zeroed, modified).
  344. Arguments:
  345. ListHead - Supplies the list of the list in which to insert the
  346. specified physical page.
  347. PageFrameIndex - Supplies the physical page number to insert in the list.
  348. Return Value:
  349. None.
  350. Environment:
  351. Must be holding the PFN database lock with APCs disabled.
  352. --*/
  353. {
  354. PFN_NUMBER last;
  355. PMMPFN Pfn1;
  356. PMMPFN Pfn2;
  357. ULONG Color;
  358. MMLISTS ListName;
  359. #if MI_BARRIER_SUPPORTED
  360. ULONG BarrierStamp;
  361. #endif
  362. ASSERT (ListHead != &MmFreePageListHead);
  363. MM_PFN_LOCK_ASSERT();
  364. ASSERT ((PageFrameIndex != 0) &&
  365. (PageFrameIndex <= MmHighestPhysicalPage) &&
  366. (PageFrameIndex >= MmLowestPhysicalPage));
  367. Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
  368. ASSERT (Pfn1->u3.e1.LockCharged == 0);
  369. PERFINFO_INSERTINLIST(PageFrameIndex, ListHead);
  370. ListName = ListHead->ListName;
  371. MI_SNAP_DATA (Pfn1, Pfn1->PteAddress, 0x20 + ListName);
  372. MI_SNAP_PFN(Pfn1, ListName, 0x2);
  373. #if DBG
  374. if (MmDebug & MM_DBG_PAGE_REF_COUNT) {
  375. if ((ListName == StandbyPageList) || (ListName == ModifiedPageList)) {
  376. PMMPTE PointerPte;
  377. PEPROCESS Process;
  378. if ((Pfn1->u3.e1.PrototypePte == 1) &&
  379. (MmIsAddressValid (Pfn1->PteAddress))) {
  380. Process = NULL;
  381. PointerPte = Pfn1->PteAddress;
  382. }
  383. else {
  384. //
  385. // The page containing the prototype PTE is not valid,
  386. // map the page into hyperspace and reference it that way.
  387. //
  388. Process = PsGetCurrentProcess ();
  389. PointerPte = MiMapPageInHyperSpaceAtDpc (Process, Pfn1->u4.PteFrame);
  390. PointerPte = (PMMPTE)((PCHAR)PointerPte +
  391. MiGetByteOffset(Pfn1->PteAddress));
  392. }
  393. ASSERT ((MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerPte) == PageFrameIndex) ||
  394. (MI_GET_PAGE_FRAME_FROM_PTE (PointerPte) == PageFrameIndex));
  395. ASSERT (PointerPte->u.Soft.Transition == 1);
  396. ASSERT (PointerPte->u.Soft.Prototype == 0);
  397. if (Process != NULL) {
  398. MiUnmapPageInHyperSpaceFromDpc (Process, PointerPte);
  399. }
  400. }
  401. }
  402. if ((ListName == StandbyPageList) || (ListName == ModifiedPageList)) {
  403. if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
  404. (Pfn1->OriginalPte.u.Soft.Transition == 1)) {
  405. KeBugCheckEx (MEMORY_MANAGEMENT, 0x8888, 0,0,0);
  406. }
  407. }
  408. #endif
  409. //
  410. // Check to ensure the reference count for the page is zero.
  411. //
  412. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  413. if (Pfn1->u3.e1.Rom == 1) {
  414. //
  415. // ROM pages do not go on transition lists and are not counted towards
  416. // MmAvailablePages as they are not reusable. Migrate these pages
  417. // into a separate list but keep the PageLocation as standby.
  418. //
  419. ASSERT (XIPConfigured == TRUE);
  420. ASSERT (Pfn1->u3.e1.Modified == 0);
  421. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  422. ListHead = &MmRomPageListHead;
  423. ListHead->Total += 1; // One more page on the list.
  424. last = ListHead->Blink;
  425. if (last != MM_EMPTY_LIST) {
  426. Pfn2 = MI_PFN_ELEMENT (last);
  427. Pfn2->u1.Flink = PageFrameIndex;
  428. }
  429. else {
  430. //
  431. // List is empty, add the page to the ListHead.
  432. //
  433. ListHead->Flink = PageFrameIndex;
  434. }
  435. ListHead->Blink = PageFrameIndex;
  436. Pfn1->u1.Flink = MM_EMPTY_LIST;
  437. Pfn1->u2.Blink = last;
  438. Pfn1->u3.e1.PageLocation = ListName;
  439. return;
  440. }
  441. ListHead->Total += 1; // One more page on the list.
  442. if (ListHead == &MmModifiedPageListHead) {
  443. //
  444. // Leave the page as cached as it may need to be written out at some
  445. // point and presumably the driver stack will need to map it at that
  446. // time.
  447. //
  448. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  449. //
  450. // On MIPS R4000 modified pages destined for the paging file are
  451. // kept on separate lists which group pages of the same color
  452. // together.
  453. //
  454. if (Pfn1->OriginalPte.u.Soft.Prototype == 0) {
  455. //
  456. // This page is destined for the paging file (not
  457. // a mapped file). Change the list head to the
  458. // appropriate colored list head.
  459. //
  460. #if MM_MAXIMUM_NUMBER_OF_COLORS > 1
  461. ListHead = &MmModifiedPageListByColor [Pfn1->u3.e1.PageColor];
  462. #else
  463. ListHead = &MmModifiedPageListByColor [0];
  464. #endif
  465. ASSERT (ListHead->ListName == ListName);
  466. ListHead->Total += 1;
  467. MmTotalPagesForPagingFile += 1;
  468. }
  469. else {
  470. //
  471. // This page is destined for a mapped file (not
  472. // the paging file). If there are no other pages currently
  473. // destined for the mapped file, start our timer so that we can
  474. // ensure that these pages make it to disk even if we don't pile
  475. // up enough of them to trigger the modified page writer or need
  476. // the memory. If we don't do this here, then for this scenario,
  477. // only an orderly system shutdown will write them out (days,
  478. // weeks, months or years later) and any power out in between
  479. // means we'll have lost the data.
  480. //
  481. if (ListHead->Total - MmTotalPagesForPagingFile == 1) {
  482. //
  483. // Start the DPC timer because we're the first on the list.
  484. //
  485. if (MiTimerPending == FALSE) {
  486. MiTimerPending = TRUE;
  487. KeSetTimerEx (&MiModifiedPageWriterTimer,
  488. MiModifiedPageLife,
  489. 0,
  490. &MiModifiedPageWriterTimerDpc);
  491. }
  492. }
  493. }
  494. }
  495. else if ((Pfn1->u3.e1.RemovalRequested == 1) &&
  496. (ListName <= StandbyPageList)) {
  497. ListHead->Total -= 1; // Undo previous increment
  498. if (ListName == StandbyPageList) {
  499. Pfn1->u3.e1.PageLocation = StandbyPageList;
  500. MiRestoreTransitionPte (PageFrameIndex);
  501. }
  502. Pfn1->u3.e1.CacheAttribute = MiNotMapped;
  503. ListHead = &MmBadPageListHead;
  504. ASSERT (ListHead->ListName == BadPageList);
  505. ListHead->Total += 1; // One more page on the list.
  506. ListName = BadPageList;
  507. }
  508. last = ListHead->Blink;
  509. if (last != MM_EMPTY_LIST) {
  510. Pfn2 = MI_PFN_ELEMENT (last);
  511. Pfn2->u1.Flink = PageFrameIndex;
  512. }
  513. else {
  514. //
  515. // List is empty, add the page to the ListHead.
  516. //
  517. ListHead->Flink = PageFrameIndex;
  518. }
  519. ListHead->Blink = PageFrameIndex;
  520. Pfn1->u1.Flink = MM_EMPTY_LIST;
  521. Pfn1->u2.Blink = last;
  522. Pfn1->u3.e1.PageLocation = ListName;
  523. //
  524. // If the page was placed on the standby or zeroed list,
  525. // update the count of usable pages in the system. If the count
  526. // transitions from 0 to 1, the event associated with available
  527. // pages should become true.
  528. //
  529. if (ListName <= StandbyPageList) {
  530. MmAvailablePages += 1;
  531. //
  532. // A page has just become available, check to see if the
  533. // page wait events should be signaled.
  534. //
  535. if (MmAvailablePages <= MM_HIGH_LIMIT) {
  536. if (MmAvailablePages == MM_HIGH_LIMIT) {
  537. KeSetEvent (&MmAvailablePagesEventHigh, 0, FALSE);
  538. }
  539. else if (MmAvailablePages == MM_MEDIUM_LIMIT) {
  540. KeSetEvent (&MmAvailablePagesEventMedium, 0, FALSE);
  541. }
  542. else if (MmAvailablePages == MM_LOW_LIMIT) {
  543. KeSetEvent (&MmAvailablePagesEvent, 0, FALSE);
  544. }
  545. }
  546. //
  547. // Signal applications if the freed page crosses a threshold.
  548. //
  549. if (MmAvailablePages == MmLowMemoryThreshold) {
  550. KeClearEvent (MiLowMemoryEvent);
  551. }
  552. else if (MmAvailablePages == MmHighMemoryThreshold) {
  553. KeSetEvent (MiHighMemoryEvent, 0, FALSE);
  554. }
  555. if (ListName <= FreePageList) {
  556. PMMCOLOR_TABLES ColorHead;
  557. ASSERT (ListName == ZeroedPageList);
  558. ASSERT (Pfn1->u4.InPageError == 0);
  559. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  560. #if defined(MI_MULTINODE)
  561. //
  562. // Increment the zero page count for this node.
  563. //
  564. if (KeNumberNodes > 1) {
  565. KeNodeBlock[Pfn1->u3.e1.PageColor]->FreeCount[ListName]++;
  566. }
  567. #endif
  568. //
  569. // We are adding a page to the zeroed page list.
  570. // Add the page to the end of the correct colored page list.
  571. //
  572. Color = MI_GET_COLOR_FROM_LIST_ENTRY(PageFrameIndex, Pfn1);
  573. ColorHead = &MmFreePagesByColor[ListName][Color];
  574. if (ColorHead->Flink == MM_EMPTY_LIST) {
  575. //
  576. // This list is empty, add this as the first and last
  577. // entry.
  578. //
  579. ColorHead->Flink = PageFrameIndex;
  580. ColorHead->Blink = (PVOID)Pfn1;
  581. }
  582. else {
  583. Pfn2 = (PMMPFN)ColorHead->Blink;
  584. Pfn2->OriginalPte.u.Long = PageFrameIndex;
  585. ColorHead->Blink = (PVOID)Pfn1;
  586. }
  587. Pfn1->OriginalPte.u.Long = MM_EMPTY_LIST;
  588. ColorHead->Count += 1;
  589. #if MI_BARRIER_SUPPORTED
  590. if (ListName == ZeroedPageList) {
  591. MI_BARRIER_STAMP_ZEROED_PAGE (&BarrierStamp);
  592. Pfn1->u4.PteFrame = BarrierStamp;
  593. }
  594. #endif
  595. }
  596. else {
  597. //
  598. // Transition page list so tally it appropriately.
  599. //
  600. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  601. MI_TALLY_TRANSITION_PAGE_ADDITION (Pfn1);
  602. }
  603. return;
  604. }
  605. //
  606. // Check to see if there are too many modified pages.
  607. //
  608. if (ListName == ModifiedPageList) {
  609. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  610. //
  611. // Transition page list so tally it appropriately.
  612. //
  613. MI_TALLY_TRANSITION_PAGE_ADDITION (Pfn1);
  614. if (Pfn1->OriginalPte.u.Soft.Prototype == 0) {
  615. ASSERT (Pfn1->OriginalPte.u.Soft.PageFileHigh == 0);
  616. }
  617. PsGetCurrentProcess()->ModifiedPageCount += 1;
  618. if ((MmModifiedPageListHead.Total >= MmModifiedPageMaximum) &&
  619. (MmAvailablePages < MmMoreThanEnoughFreePages)) {
  620. //
  621. // Start the modified page writer.
  622. //
  623. KeSetEvent (&MmModifiedPageWriterEvent, 0, FALSE);
  624. }
  625. }
  626. else if (ListName == ModifiedNoWritePageList) {
  627. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  628. MI_TALLY_TRANSITION_PAGE_ADDITION (Pfn1);
  629. }
  630. return;
  631. }
  632. VOID
  633. FASTCALL
  634. MiInsertStandbyListAtFront (
  635. IN PFN_NUMBER PageFrameIndex
  636. )
  637. /*++
  638. Routine Description:
  639. This procedure inserts a page at the front of the standby list.
  640. Arguments:
  641. PageFrameIndex - Supplies the physical page number to insert in the list.
  642. Return Value:
  643. None.
  644. Environment:
  645. PFN lock held.
  646. --*/
  647. {
  648. PFN_NUMBER first;
  649. PFN_NUMBER last;
  650. IN PMMPFNLIST ListHead;
  651. PMMPFN Pfn1;
  652. PMMPFN Pfn2;
  653. MM_PFN_LOCK_ASSERT();
  654. ASSERT ((PageFrameIndex != 0) && (PageFrameIndex <= MmHighestPhysicalPage) &&
  655. (PageFrameIndex >= MmLowestPhysicalPage));
  656. Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
  657. MI_SNAP_DATA (Pfn1, Pfn1->PteAddress, 9);
  658. PERFINFO_INSERT_FRONT_STANDBY(PageFrameIndex);
  659. #if DBG
  660. if (MmDebug & MM_DBG_PAGE_REF_COUNT) {
  661. PMMPTE PointerPte;
  662. PEPROCESS Process;
  663. if ((Pfn1->u3.e1.PrototypePte == 1) &&
  664. (MmIsAddressValid (Pfn1->PteAddress))) {
  665. PointerPte = Pfn1->PteAddress;
  666. Process = NULL;
  667. }
  668. else {
  669. //
  670. // The page containing the prototype PTE is not valid,
  671. // map the page into hyperspace and reference it that way.
  672. //
  673. Process = PsGetCurrentProcess ();
  674. PointerPte = MiMapPageInHyperSpaceAtDpc (Process, Pfn1->u4.PteFrame);
  675. PointerPte = (PMMPTE)((PCHAR)PointerPte +
  676. MiGetByteOffset(Pfn1->PteAddress));
  677. }
  678. ASSERT ((MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (PointerPte) == PageFrameIndex) ||
  679. (MI_GET_PAGE_FRAME_FROM_PTE (PointerPte) == PageFrameIndex));
  680. ASSERT (PointerPte->u.Soft.Transition == 1);
  681. ASSERT (PointerPte->u.Soft.Prototype == 0);
  682. if (Process != NULL) {
  683. MiUnmapPageInHyperSpaceFromDpc (Process, PointerPte);
  684. }
  685. }
  686. if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
  687. (Pfn1->OriginalPte.u.Soft.Transition == 1)) {
  688. KeBugCheckEx (MEMORY_MANAGEMENT, 0x8889, 0,0,0);
  689. }
  690. #endif
  691. //
  692. // Check to ensure the reference count for the page is zero.
  693. //
  694. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  695. ASSERT (Pfn1->u3.e1.PrototypePte == 1);
  696. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  697. if (Pfn1->u3.e1.Rom == 1) {
  698. //
  699. // ROM pages do not go on transition lists and are not counted towards
  700. // MmAvailablePages as they are not reusable. Migrate these pages
  701. // into a separate list but keep the PageLocation as standby. Note
  702. // it doesn't matter if the page is put at the head or the tail since
  703. // it's not reusable.
  704. //
  705. ASSERT (XIPConfigured == TRUE);
  706. ASSERT (Pfn1->u3.e1.Modified == 0);
  707. ListHead = &MmRomPageListHead;
  708. ListHead->Total += 1; // One more page on the list.
  709. last = ListHead->Blink;
  710. if (last != MM_EMPTY_LIST) {
  711. Pfn2 = MI_PFN_ELEMENT (last);
  712. Pfn2->u1.Flink = PageFrameIndex;
  713. }
  714. else {
  715. //
  716. // List is empty, add the page to the ListHead.
  717. //
  718. ListHead->Flink = PageFrameIndex;
  719. }
  720. ListHead->Blink = PageFrameIndex;
  721. Pfn1->u1.Flink = MM_EMPTY_LIST;
  722. Pfn1->u2.Blink = last;
  723. Pfn1->u3.e1.PageLocation = StandbyPageList;
  724. return;
  725. }
  726. MmTransitionSharedPages += 1;
  727. MmStandbyPageListHead.Total += 1; // One more page on the list.
  728. ASSERT (MmTransitionPrivatePages + MmTransitionSharedPages == MmStandbyPageListHead.Total + MmModifiedPageListHead.Total + MmModifiedNoWritePageListHead.Total);
  729. ListHead = &MmStandbyPageListHead;
  730. first = ListHead->Flink;
  731. if (first == MM_EMPTY_LIST) {
  732. //
  733. // List is empty add the page to the ListHead.
  734. //
  735. ListHead->Blink = PageFrameIndex;
  736. }
  737. else {
  738. Pfn2 = MI_PFN_ELEMENT (first);
  739. Pfn2->u2.Blink = PageFrameIndex;
  740. }
  741. ListHead->Flink = PageFrameIndex;
  742. Pfn1->u2.Blink = MM_EMPTY_LIST;
  743. Pfn1->u1.Flink = first;
  744. Pfn1->u3.e1.PageLocation = StandbyPageList;
  745. //
  746. // If the page was placed on the free, standby or zeroed list,
  747. // update the count of usable pages in the system. If the count
  748. // transitions from 0 to 1, the event associated with available
  749. // pages should become true.
  750. //
  751. MmAvailablePages += 1;
  752. //
  753. // A page has just become available, check to see if the
  754. // page wait events should be signalled.
  755. //
  756. if (MmAvailablePages <= MM_HIGH_LIMIT) {
  757. if (MmAvailablePages == MM_HIGH_LIMIT) {
  758. KeSetEvent (&MmAvailablePagesEventHigh, 0, FALSE);
  759. }
  760. else if (MmAvailablePages == MM_MEDIUM_LIMIT) {
  761. KeSetEvent (&MmAvailablePagesEventMedium, 0, FALSE);
  762. }
  763. else if (MmAvailablePages == MM_LOW_LIMIT) {
  764. KeSetEvent (&MmAvailablePagesEvent, 0, FALSE);
  765. }
  766. }
  767. //
  768. // Signal applications if the freed page crosses a threshold.
  769. //
  770. if (MmAvailablePages == MmLowMemoryThreshold) {
  771. KeClearEvent (MiLowMemoryEvent);
  772. }
  773. else if (MmAvailablePages == MmHighMemoryThreshold) {
  774. KeSetEvent (MiHighMemoryEvent, 0, FALSE);
  775. }
  776. return;
  777. }
  778. PFN_NUMBER
  779. FASTCALL
  780. MiRemovePageFromList (
  781. IN PMMPFNLIST ListHead
  782. )
  783. /*++
  784. Routine Description:
  785. This procedure removes a page from the head of the specified list (free,
  786. standby or zeroed).
  787. This routine clears the flags word in the PFN database, hence the
  788. PFN information for this page must be initialized.
  789. Arguments:
  790. ListHead - Supplies the list of the list in which to remove the
  791. specified physical page.
  792. Return Value:
  793. The physical page number removed from the specified list.
  794. Environment:
  795. PFN lock held.
  796. --*/
  797. {
  798. PMMCOLOR_TABLES ColorHead;
  799. PFN_NUMBER PageFrameIndex;
  800. PMMPFN Pfn1;
  801. PMMPFN Pfn2;
  802. ULONG Color;
  803. MMLISTS ListName;
  804. MM_PFN_LOCK_ASSERT();
  805. //
  806. // If the specified list is empty return MM_EMPTY_LIST.
  807. //
  808. if (ListHead->Total == 0) {
  809. KeBugCheckEx (PFN_LIST_CORRUPT, 1, (ULONG_PTR)ListHead, MmAvailablePages, 0);
  810. }
  811. ListName = ListHead->ListName;
  812. ASSERT (ListName != ModifiedPageList);
  813. //
  814. // Decrement the count of pages on the list and remove the first
  815. // page from the list.
  816. //
  817. ListHead->Total -= 1;
  818. PageFrameIndex = ListHead->Flink;
  819. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  820. if (PERFINFO_IS_GROUP_ON(PERF_MEMORY)) {
  821. MiLogPfnInformation (Pfn1, PERFINFO_LOG_TYPE_REMOVEPAGEFROMLIST);
  822. }
  823. ListHead->Flink = Pfn1->u1.Flink;
  824. //
  825. // Zero the flink and blink in the PFN database element.
  826. //
  827. Pfn1->u1.Flink = 0; // Assumes Flink width is >= WsIndex width
  828. Pfn1->u2.Blink = 0;
  829. //
  830. // If the last page was removed (the ListHead->Flink is now
  831. // MM_EMPTY_LIST) make the Listhead->Blink MM_EMPTY_LIST as well.
  832. //
  833. if (ListHead->Flink != MM_EMPTY_LIST) {
  834. //
  835. // Make the PFN element blink point to MM_EMPTY_LIST signifying this
  836. // is the first page in the list.
  837. //
  838. Pfn2 = MI_PFN_ELEMENT (ListHead->Flink);
  839. Pfn2->u2.Blink = MM_EMPTY_LIST;
  840. }
  841. else {
  842. ListHead->Blink = MM_EMPTY_LIST;
  843. }
  844. //
  845. // Check to see if we now have one less page available.
  846. //
  847. if (ListName <= StandbyPageList) {
  848. MmAvailablePages -= 1;
  849. if (ListName == StandbyPageList) {
  850. //
  851. // This page is currently in transition, restore the PTE to
  852. // its original contents so this page can be reused.
  853. //
  854. MI_TALLY_TRANSITION_PAGE_REMOVAL (Pfn1);
  855. MiRestoreTransitionPte (PageFrameIndex);
  856. }
  857. if (MmAvailablePages < MmMinimumFreePages) {
  858. //
  859. // Obtain free pages.
  860. //
  861. MiObtainFreePages();
  862. }
  863. }
  864. ASSERT ((PageFrameIndex != 0) &&
  865. (PageFrameIndex <= MmHighestPhysicalPage) &&
  866. (PageFrameIndex >= MmLowestPhysicalPage));
  867. //
  868. // Zero the PFN flags longword.
  869. //
  870. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  871. Color = Pfn1->u3.e1.PageColor;
  872. ASSERT (Pfn1->u3.e1.RemovalRequested == 0);
  873. ASSERT (Pfn1->u3.e1.Rom == 0);
  874. Pfn1->u3.e2.ShortFlags = 0;
  875. Pfn1->u3.e1.PageColor = Color;
  876. Pfn1->u3.e1.CacheAttribute = MiNotMapped;
  877. if (ListName <= FreePageList) {
  878. //
  879. // Update the color lists.
  880. //
  881. Color = MI_GET_COLOR_FROM_LIST_ENTRY(PageFrameIndex, Pfn1);
  882. ColorHead = &MmFreePagesByColor[ListName][Color];
  883. ASSERT (ColorHead->Flink == PageFrameIndex);
  884. ColorHead->Flink = (PFN_NUMBER) Pfn1->OriginalPte.u.Long;
  885. ASSERT (ColorHead->Count >= 1);
  886. ColorHead->Count -= 1;
  887. }
  888. return PageFrameIndex;
  889. }
  890. VOID
  891. FASTCALL
  892. MiUnlinkPageFromList (
  893. IN PMMPFN Pfn
  894. )
  895. /*++
  896. Routine Description:
  897. This procedure removes a page from the middle of a list. This is
  898. designed for the faulting of transition pages from the standby and
  899. modified list and making them active and valid again.
  900. Arguments:
  901. Pfn - Supplies a pointer to the PFN database element for the physical
  902. page to remove from the list.
  903. Return Value:
  904. none.
  905. Environment:
  906. Must be holding the PFN database lock with APCs disabled.
  907. --*/
  908. {
  909. PMMPFNLIST ListHead;
  910. PFN_NUMBER Previous;
  911. PFN_NUMBER Next;
  912. PMMPFN Pfn2;
  913. MMLISTS ListName;
  914. MM_PFN_LOCK_ASSERT();
  915. PERFINFO_UNLINKPAGE((ULONG_PTR)(Pfn - MmPfnDatabase), Pfn->u3.e1.PageLocation);
  916. //
  917. // Page not on standby or modified list, check to see if the
  918. // page is currently being written by the modified page
  919. // writer, if so, just return this page. The reference
  920. // count for the page will be incremented, so when the modified
  921. // page write completes, the page will not be put back on
  922. // the list, rather, it will remain active and valid.
  923. //
  924. if (Pfn->u3.e2.ReferenceCount > 0) {
  925. //
  926. // The page was not on any "transition lists", check to see
  927. // if this has I/O in progress.
  928. //
  929. if (Pfn->u2.ShareCount == 0) {
  930. #if DBG
  931. if (MmDebug & MM_DBG_PAGE_IN_LIST) {
  932. DbgPrint("unlinking page not in list...\n");
  933. MiFormatPfn(Pfn);
  934. }
  935. #endif
  936. return;
  937. }
  938. KdPrint(("MM:attempt to remove page from wrong page list\n"));
  939. KeBugCheckEx (PFN_LIST_CORRUPT,
  940. 2,
  941. Pfn - MmPfnDatabase,
  942. MmHighestPhysicalPage,
  943. Pfn->u3.e2.ReferenceCount);
  944. }
  945. ListHead = MmPageLocationList[Pfn->u3.e1.PageLocation];
  946. //
  947. // Must not remove pages from free or zeroed without updating
  948. // the colored lists.
  949. //
  950. ListName = ListHead->ListName;
  951. ASSERT (ListName >= StandbyPageList);
  952. //
  953. // If memory mirroring is in progress, any additions or removals to the
  954. // free, zeroed, standby, modified or modified-no-write lists must
  955. // update the bitmap.
  956. //
  957. if (MiMirroringActive == TRUE) {
  958. RtlSetBit (MiMirrorBitMap2, (ULONG)(Pfn - MmPfnDatabase));
  959. }
  960. ASSERT (Pfn->u3.e1.CacheAttribute == MiCached);
  961. if (ListHead == &MmStandbyPageListHead) {
  962. if (Pfn->u3.e1.Rom == 1) {
  963. ASSERT (XIPConfigured == TRUE);
  964. ASSERT (Pfn->u3.e1.Modified == 0);
  965. ListHead = &MmRomPageListHead;
  966. //
  967. // Deliberately switch to an invalid page listname so the checks
  968. // below do not alter available pages, etc.
  969. //
  970. ListName = ActiveAndValid;
  971. }
  972. }
  973. else if ((ListHead == &MmModifiedPageListHead) &&
  974. (Pfn->OriginalPte.u.Soft.Prototype == 0)) {
  975. //
  976. // On MIPS R4000 modified pages destined for the paging file are
  977. // kept on separate lists which group pages of the same color
  978. // together.
  979. //
  980. //
  981. // This page is destined for the paging file (not
  982. // a mapped file). Change the list head to the
  983. // appropriate colored list head.
  984. //
  985. ListHead->Total -= 1;
  986. MmTotalPagesForPagingFile -= 1;
  987. #if MM_MAXIMUM_NUMBER_OF_COLORS > 1
  988. ListHead = &MmModifiedPageListByColor [Pfn->u3.e1.PageColor];
  989. #else
  990. ListHead = &MmModifiedPageListByColor [0];
  991. #endif
  992. ASSERT (ListHead->ListName == ListName);
  993. }
  994. ASSERT (Pfn->u3.e1.WriteInProgress == 0);
  995. ASSERT (Pfn->u3.e1.ReadInProgress == 0);
  996. ASSERT (ListHead->Total != 0);
  997. Next = Pfn->u1.Flink;
  998. Pfn->u1.Flink = 0; // Assumes Flink width is >= WsIndex width
  999. Previous = Pfn->u2.Blink;
  1000. Pfn->u2.Blink = 0;
  1001. if (Next != MM_EMPTY_LIST) {
  1002. Pfn2 = MI_PFN_ELEMENT(Next);
  1003. Pfn2->u2.Blink = Previous;
  1004. }
  1005. else {
  1006. ListHead->Blink = Previous;
  1007. }
  1008. if (Previous != MM_EMPTY_LIST) {
  1009. Pfn2 = MI_PFN_ELEMENT(Previous);
  1010. Pfn2->u1.Flink = Next;
  1011. }
  1012. else {
  1013. ListHead->Flink = Next;
  1014. }
  1015. ListHead->Total -= 1;
  1016. //
  1017. // Check to see if we now have one less page available.
  1018. //
  1019. if (ListName <= StandbyPageList) {
  1020. MmAvailablePages -= 1;
  1021. if (ListName == StandbyPageList) {
  1022. MI_TALLY_TRANSITION_PAGE_REMOVAL (Pfn);
  1023. }
  1024. if (MmAvailablePages < MmMinimumFreePages) {
  1025. //
  1026. // Obtain free pages.
  1027. //
  1028. MiObtainFreePages();
  1029. }
  1030. }
  1031. else if (ListName == ModifiedPageList || ListName == ModifiedNoWritePageList) {
  1032. MI_TALLY_TRANSITION_PAGE_REMOVAL (Pfn);
  1033. }
  1034. return;
  1035. }
  1036. VOID
  1037. MiUnlinkFreeOrZeroedPage (
  1038. IN PFN_NUMBER Page
  1039. )
  1040. /*++
  1041. Routine Description:
  1042. This procedure removes a page from the middle of a list. This is
  1043. designed for the removing of free or zeroed pages from the middle of
  1044. their lists.
  1045. Arguments:
  1046. Page - Supplies a page frame index to remove from the list.
  1047. Return Value:
  1048. None.
  1049. Environment:
  1050. Must be holding the PFN database lock with APCs disabled.
  1051. --*/
  1052. {
  1053. PMMPFNLIST ListHead;
  1054. PFN_NUMBER Previous;
  1055. PFN_NUMBER Next;
  1056. PMMPFN Pfn2;
  1057. PMMPFN Pfn;
  1058. ULONG Color;
  1059. PMMCOLOR_TABLES ColorHead;
  1060. MMLISTS ListName;
  1061. Pfn = MI_PFN_ELEMENT (Page);
  1062. MM_PFN_LOCK_ASSERT();
  1063. ListHead = MmPageLocationList[Pfn->u3.e1.PageLocation];
  1064. ListName = ListHead->ListName;
  1065. ASSERT (ListHead->Total != 0);
  1066. ListHead->Total -= 1;
  1067. ASSERT (ListName <= FreePageList);
  1068. ASSERT (Pfn->u3.e1.WriteInProgress == 0);
  1069. ASSERT (Pfn->u3.e1.ReadInProgress == 0);
  1070. ASSERT (Pfn->u3.e1.CacheAttribute == MiNotMapped);
  1071. //
  1072. // If memory mirroring is in progress, any removals from the
  1073. // free, zeroed, standby, modified or modified-no-write lists that
  1074. // isn't immediately re-inserting into one of these 5 lists (WITHOUT
  1075. // modifying the page contents) must update the bitmap.
  1076. //
  1077. if (MiMirroringActive == TRUE) {
  1078. RtlSetBit (MiMirrorBitMap2, (ULONG)Page);
  1079. }
  1080. PERFINFO_UNLINKFREEPAGE (Page, Pfn->u3.e1.PageLocation);
  1081. Next = Pfn->u1.Flink;
  1082. Pfn->u1.Flink = 0; // Assumes Flink width is >= WsIndex width
  1083. Previous = Pfn->u2.Blink;
  1084. Pfn->u2.Blink = 0;
  1085. if (Next != MM_EMPTY_LIST) {
  1086. Pfn2 = MI_PFN_ELEMENT(Next);
  1087. Pfn2->u2.Blink = Previous;
  1088. }
  1089. else {
  1090. ListHead->Blink = Previous;
  1091. }
  1092. if (Previous == MM_EMPTY_LIST) {
  1093. ListHead->Flink = Next;
  1094. }
  1095. else {
  1096. Pfn2 = MI_PFN_ELEMENT(Previous);
  1097. Pfn2->u1.Flink = Next;
  1098. }
  1099. //
  1100. // We are removing a page from the middle of the free or zeroed page list.
  1101. // The secondary color tables must be updated at this time.
  1102. //
  1103. Color = MI_GET_COLOR_FROM_LIST_ENTRY(Page, Pfn);
  1104. //
  1105. // Walk down the list and remove the page.
  1106. //
  1107. ColorHead = &MmFreePagesByColor[ListName][Color];
  1108. Next = ColorHead->Flink;
  1109. if (Next == Page) {
  1110. ColorHead->Flink = (PFN_NUMBER) Pfn->OriginalPte.u.Long;
  1111. }
  1112. else {
  1113. //
  1114. // Walk the list to find the parent.
  1115. //
  1116. do {
  1117. Pfn2 = MI_PFN_ELEMENT (Next);
  1118. Next = (PFN_NUMBER) Pfn2->OriginalPte.u.Long;
  1119. if (Page == Next) {
  1120. Pfn2->OriginalPte.u.Long = Pfn->OriginalPte.u.Long;
  1121. if ((PFN_NUMBER) Pfn->OriginalPte.u.Long == MM_EMPTY_LIST) {
  1122. ColorHead->Blink = Pfn2;
  1123. }
  1124. break;
  1125. }
  1126. } while (TRUE);
  1127. }
  1128. ASSERT (ColorHead->Count >= 1);
  1129. ColorHead->Count -= 1;
  1130. //
  1131. // Decrement availability count.
  1132. //
  1133. #if defined(MI_MULTINODE)
  1134. if (KeNumberNodes > 1) {
  1135. MI_NODE_FROM_COLOR(Color)->FreeCount[ListName]--;
  1136. }
  1137. #endif
  1138. MmAvailablePages -= 1;
  1139. if (MmAvailablePages < MmMinimumFreePages) {
  1140. //
  1141. // Obtain free pages.
  1142. //
  1143. MiObtainFreePages();
  1144. }
  1145. return;
  1146. }
  1147. ULONG
  1148. FASTCALL
  1149. MiEnsureAvailablePageOrWait (
  1150. IN PEPROCESS Process,
  1151. IN PVOID VirtualAddress
  1152. )
  1153. /*++
  1154. Routine Description:
  1155. This procedure ensures that a physical page is available on
  1156. the zeroed, free or standby list such that the next call the remove a
  1157. page absolutely will not block. This is necessary as blocking would
  1158. require a wait which could cause a deadlock condition.
  1159. If a page is available the function returns immediately with a value
  1160. of FALSE indicating no wait operation was performed. If no physical
  1161. page is available, the thread enters a wait state and the function
  1162. returns the value TRUE when the wait operation completes.
  1163. Arguments:
  1164. Process - Supplies a pointer to the current process if, and only if,
  1165. the working set mutex is held currently held and should
  1166. be released if a wait operation is issued. Supplies
  1167. the value NULL otherwise.
  1168. VirtualAddress - Supplies the virtual address for the faulting page.
  1169. If the value is NULL, the page is treated as a
  1170. user mode address.
  1171. Return Value:
  1172. FALSE - if a page was immediately available.
  1173. TRUE - if a wait operation occurred before a page became available.
  1174. Environment:
  1175. Must be holding the PFN database lock with APCs disabled.
  1176. --*/
  1177. {
  1178. PVOID Event;
  1179. NTSTATUS Status;
  1180. KIRQL OldIrql;
  1181. KIRQL Ignore;
  1182. ULONG Limit;
  1183. ULONG Relock;
  1184. PFN_NUMBER StrandedPages;
  1185. LOGICAL WsHeldSafe;
  1186. PMMPFN Pfn1;
  1187. PMMPFN EndPfn;
  1188. LARGE_INTEGER WaitBegin;
  1189. LARGE_INTEGER WaitEnd;
  1190. MM_PFN_LOCK_ASSERT();
  1191. if (MmAvailablePages >= MM_HIGH_LIMIT) {
  1192. //
  1193. // Pages are available.
  1194. //
  1195. return FALSE;
  1196. }
  1197. //
  1198. // If this fault is for paged pool (or pagable kernel space,
  1199. // including page table pages), let it use the last page.
  1200. //
  1201. #if defined(_IA64_)
  1202. if (MI_IS_SYSTEM_ADDRESS(VirtualAddress) ||
  1203. (MI_IS_HYPER_SPACE_ADDRESS(VirtualAddress))) {
  1204. #else
  1205. if (((PMMPTE)VirtualAddress > MiGetPteAddress(HYPER_SPACE)) ||
  1206. ((VirtualAddress > MM_HIGHEST_USER_ADDRESS) &&
  1207. (VirtualAddress < (PVOID)PTE_BASE))) {
  1208. #endif
  1209. //
  1210. // This fault is in the system, use 1 page as the limit.
  1211. //
  1212. if (MmAvailablePages >= MM_LOW_LIMIT) {
  1213. //
  1214. // Pages are available.
  1215. //
  1216. return FALSE;
  1217. }
  1218. Limit = MM_LOW_LIMIT;
  1219. Event = (PVOID)&MmAvailablePagesEvent;
  1220. }
  1221. else {
  1222. //
  1223. // If this thread has explicitly disabled APCs (FsRtlEnterFileSystem
  1224. // does this), then it may be holding resources or mutexes that may in
  1225. // turn be blocking memory making threads from making progress.
  1226. //
  1227. // Likewise give system threads a free pass as they may be worker
  1228. // threads processing potentially blocking items drivers have queued.
  1229. //
  1230. if ((IS_SYSTEM_THREAD(PsGetCurrentThread())) ||
  1231. (KeGetCurrentThread()->KernelApcDisable != 0)) {
  1232. if (MmAvailablePages >= MM_MEDIUM_LIMIT) {
  1233. //
  1234. // Pages are available.
  1235. //
  1236. return FALSE;
  1237. }
  1238. Limit = MM_MEDIUM_LIMIT;
  1239. Event = (PVOID) &MmAvailablePagesEventMedium;
  1240. }
  1241. else {
  1242. Limit = MM_HIGH_LIMIT;
  1243. Event = (PVOID) &MmAvailablePagesEventHigh;
  1244. }
  1245. }
  1246. //
  1247. // Initializing WsHeldSafe is not needed for
  1248. // correctness but without it the compiler cannot compile this code
  1249. // W4 to check for use of uninitialized variables.
  1250. //
  1251. WsHeldSafe = FALSE;
  1252. while (MmAvailablePages < Limit) {
  1253. KeClearEvent ((PKEVENT)Event);
  1254. UNLOCK_PFN (APC_LEVEL);
  1255. Relock = FALSE;
  1256. if (Process == HYDRA_PROCESS) {
  1257. UNLOCK_SESSION_SPACE_WS (APC_LEVEL);
  1258. }
  1259. else if (Process != NULL) {
  1260. //
  1261. // The working set lock may have been acquired safely or unsafely
  1262. // by our caller. Handle both cases here and below.
  1263. //
  1264. UNLOCK_WS_REGARDLESS (Process, WsHeldSafe);
  1265. }
  1266. else {
  1267. if (MmSystemLockOwner == PsGetCurrentThread()) {
  1268. UNLOCK_SYSTEM_WS (APC_LEVEL);
  1269. Relock = TRUE;
  1270. }
  1271. }
  1272. KiQueryInterruptTime(&WaitBegin);
  1273. //
  1274. // Wait 7 minutes for pages to become available.
  1275. //
  1276. Status = KeWaitForSingleObject(Event,
  1277. WrFreePage,
  1278. KernelMode,
  1279. FALSE,
  1280. (PLARGE_INTEGER)&MmSevenMinutes);
  1281. if (Status == STATUS_TIMEOUT) {
  1282. KiQueryInterruptTime(&WaitEnd);
  1283. if (MmSystemShutdown != 0) {
  1284. //
  1285. // Because applications are not terminated and drivers are
  1286. // not unloaded, they can continue to access pages even after
  1287. // the modified writer has terminated. This can cause the
  1288. // system to run out of pages since the pagefile(s) cannot be
  1289. // used.
  1290. //
  1291. KeBugCheckEx (DISORDERLY_SHUTDOWN,
  1292. MmModifiedPageListHead.Total,
  1293. MmTotalPagesForPagingFile,
  1294. (MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT) - MmAllocatedNonPagedPool,
  1295. MmSystemShutdown);
  1296. }
  1297. //
  1298. // See how many transition pages have nonzero reference counts as
  1299. // these indicate drivers that aren't unlocking the pages in their
  1300. // MDLs.
  1301. //
  1302. Limit = 0;
  1303. StrandedPages = 0;
  1304. do {
  1305. Pfn1 = MI_PFN_ELEMENT (MmPhysicalMemoryBlock->Run[Limit].BasePage);
  1306. EndPfn = Pfn1 + MmPhysicalMemoryBlock->Run[Limit].PageCount;
  1307. while (Pfn1 < EndPfn) {
  1308. if ((Pfn1->u3.e1.PageLocation == TransitionPage) &&
  1309. (Pfn1->u3.e2.ReferenceCount != 0)) {
  1310. StrandedPages += 1;
  1311. }
  1312. Pfn1 += 1;
  1313. }
  1314. Limit += 1;
  1315. } while (Limit != MmPhysicalMemoryBlock->NumberOfRuns);
  1316. //
  1317. // This bugcheck can occur for the following reasons:
  1318. //
  1319. // A driver has blocked, deadlocking the modified or mapped
  1320. // page writers. Examples of this include mutex deadlocks or
  1321. // accesses to paged out memory in filesystem drivers, filter
  1322. // drivers, etc. This indicates a driver bug.
  1323. //
  1324. // The storage driver(s) are not processing requests. Examples
  1325. // of this are stranded queues, non-responding drives, etc. This
  1326. // indicates a driver bug.
  1327. //
  1328. // Not enough pool is available for the storage stack to write out
  1329. // modified pages. This indicates a driver bug.
  1330. //
  1331. // A high priority realtime thread has starved the balance set
  1332. // manager from trimming pages and/or starved the modified writer
  1333. // from writing them out. This indicates a bug in the component
  1334. // that created this thread.
  1335. //
  1336. StrandedPages &= ~3;
  1337. StrandedPages |= MmSystemShutdown;
  1338. if (KdDebuggerNotPresent) {
  1339. if (MmTotalPagesForPagingFile >= (MmModifiedPageListHead.Total >> 2)) {
  1340. KeBugCheckEx (NO_PAGES_AVAILABLE,
  1341. MmModifiedPageListHead.Total,
  1342. MmTotalPagesForPagingFile,
  1343. (MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT) - MmAllocatedNonPagedPool,
  1344. StrandedPages);
  1345. }
  1346. KeBugCheckEx (DIRTY_MAPPED_PAGES_CONGESTION,
  1347. MmModifiedPageListHead.Total,
  1348. MmTotalPagesForPagingFile,
  1349. (MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT) - MmAllocatedNonPagedPool,
  1350. StrandedPages);
  1351. }
  1352. DbgPrint ("MmEnsureAvailablePageOrWait: 7 min timeout %x %x %x %x\n", WaitEnd.HighPart, WaitEnd.LowPart, WaitBegin.HighPart, WaitBegin.LowPart);
  1353. DbgPrint ("Without a debugger attached, the following bugcheck would have occurred.\n");
  1354. if (MmTotalPagesForPagingFile >= (MmModifiedPageListHead.Total >> 2)) {
  1355. DbgPrint ("%3lx ", NO_PAGES_AVAILABLE);
  1356. }
  1357. else {
  1358. DbgPrint ("%3lxp ", DIRTY_MAPPED_PAGES_CONGESTION);
  1359. }
  1360. DbgPrint ("%p %p %p %p\n",
  1361. MmModifiedPageListHead.Total,
  1362. MmTotalPagesForPagingFile,
  1363. (MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT) - MmAllocatedNonPagedPool,
  1364. StrandedPages);
  1365. //
  1366. // Pop into the debugger (even on free builds) to determine
  1367. // the cause of the starvation and march on.
  1368. //
  1369. DbgBreakPoint ();
  1370. }
  1371. if (Process == HYDRA_PROCESS) {
  1372. LOCK_SESSION_SPACE_WS (Ignore, PsGetCurrentThread ());
  1373. }
  1374. else if (Process != NULL) {
  1375. //
  1376. // The working set lock may have been acquired safely or unsafely
  1377. // by our caller. Reacquire it in the same manner our caller did.
  1378. //
  1379. LOCK_WS_REGARDLESS (Process, WsHeldSafe);
  1380. }
  1381. else {
  1382. if (Relock) {
  1383. LOCK_SYSTEM_WS (Ignore, PsGetCurrentThread ());
  1384. }
  1385. }
  1386. LOCK_PFN (OldIrql);
  1387. }
  1388. return TRUE;
  1389. }
  1390. PFN_NUMBER
  1391. FASTCALL
  1392. MiRemoveZeroPage (
  1393. IN ULONG Color
  1394. )
  1395. /*++
  1396. Routine Description:
  1397. This procedure removes a zero page from either the zeroed, free
  1398. or standby lists (in that order). If no pages exist on the zeroed
  1399. or free list a transition page is removed from the standby list
  1400. and the PTE (may be a prototype PTE) which refers to this page is
  1401. changed from transition back to its original contents.
  1402. If the page is not obtained from the zeroed list, it is zeroed.
  1403. Arguments:
  1404. Color - Supplies the page color for which this page is destined.
  1405. This is used for checking virtual address alignments to
  1406. determine if the D cache needs flushing before the page
  1407. can be reused.
  1408. The above was true when we were concerned about caches
  1409. which are virtually indexed (ie MIPS). Today we
  1410. are more concerned that we get a good usage spread across
  1411. the L2 caches of most machines. These caches are physically
  1412. indexed. By gathering pages that would have the same
  1413. index to the same color, then maximizing the color spread,
  1414. we maximize the effective use of the caches.
  1415. This has been extended for NUMA machines. The high part
  1416. of the color gives the node color (basically node number).
  1417. If we cannot allocate a page of the requested color, we
  1418. try to allocate a page on the same node before taking a
  1419. page from a different node.
  1420. Return Value:
  1421. The physical page number removed from the specified list.
  1422. Environment:
  1423. Must be holding the PFN database lock with APCs disabled.
  1424. --*/
  1425. {
  1426. PFN_NUMBER Page;
  1427. PMMPFN Pfn1;
  1428. PMMCOLOR_TABLES FreePagesByColor;
  1429. #if MI_BARRIER_SUPPORTED
  1430. ULONG BarrierStamp;
  1431. #endif
  1432. #if defined(MI_MULTINODE)
  1433. PKNODE Node;
  1434. ULONG NodeColor;
  1435. ULONG OriginalColor;
  1436. #endif
  1437. MM_PFN_LOCK_ASSERT();
  1438. ASSERT(MmAvailablePages != 0);
  1439. FreePagesByColor = MmFreePagesByColor[ZeroedPageList];
  1440. #if defined(MI_MULTINODE)
  1441. //
  1442. // Initializing Node is not needed for correctness, but without it
  1443. // the compiler cannot compile this code W4 to check for use of
  1444. // uninitialized variables.
  1445. //
  1446. Node = NULL;
  1447. NodeColor = Color & ~MmSecondaryColorMask;
  1448. OriginalColor = Color;
  1449. if (KeNumberNodes > 1) {
  1450. Node = MI_NODE_FROM_COLOR(Color);
  1451. }
  1452. do {
  1453. #endif
  1454. //
  1455. // Attempt to remove a page from the zeroed page list. If a page
  1456. // is available, then remove it and return its page frame index.
  1457. // Otherwise, attempt to remove a page from the free page list or
  1458. // the standby list.
  1459. //
  1460. // N.B. It is not necessary to change page colors even if the old
  1461. // color is not equal to the new color. The zero page thread
  1462. // ensures that all zeroed pages are removed from all caches.
  1463. //
  1464. ASSERT (Color < MmSecondaryColors);
  1465. Page = FreePagesByColor[Color].Flink;
  1466. if (Page != MM_EMPTY_LIST) {
  1467. //
  1468. // Remove the first entry on the zeroed by color list.
  1469. //
  1470. #if DBG
  1471. Pfn1 = MI_PFN_ELEMENT(Page);
  1472. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1473. #endif
  1474. ASSERT ((Pfn1->u3.e1.PageLocation == ZeroedPageList) ||
  1475. ((Pfn1->u3.e1.PageLocation == FreePageList) &&
  1476. (FreePagesByColor == MmFreePagesByColor[FreePageList])));
  1477. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1478. Page = MiRemovePageByColor (Page, Color);
  1479. ASSERT (Pfn1 == MI_PFN_ELEMENT(Page));
  1480. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1481. #if defined(MI_MULTINODE)
  1482. if (FreePagesByColor != MmFreePagesByColor[ZeroedPageList]) {
  1483. goto ZeroPage;
  1484. }
  1485. #endif
  1486. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  1487. ASSERT (Pfn1->u2.ShareCount == 0);
  1488. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1489. return Page;
  1490. }
  1491. #if defined(MI_MULTINODE)
  1492. //
  1493. // If this is a multinode machine and there are zero
  1494. // pages on this node, select another color on this
  1495. // node in preference to random selection.
  1496. //
  1497. if (KeNumberNodes > 1) {
  1498. if (Node->FreeCount[ZeroedPageList] != 0) {
  1499. Color = ((Color + 1) & MmSecondaryColorMask) | NodeColor;
  1500. ASSERT(Color != OriginalColor);
  1501. continue;
  1502. }
  1503. //
  1504. // No previously zeroed page with the specified secondary
  1505. // color exists. Since this is a multinode machine, zero
  1506. // an available local free page now instead of allocating a
  1507. // zeroed page from another node below.
  1508. //
  1509. if (Node->FreeCount[FreePageList] != 0) {
  1510. if (FreePagesByColor != MmFreePagesByColor[FreePageList]) {
  1511. FreePagesByColor = MmFreePagesByColor[FreePageList];
  1512. Color = OriginalColor;
  1513. }
  1514. else {
  1515. Color = ((Color + 1) & MmSecondaryColorMask) | NodeColor;
  1516. ASSERT(Color != OriginalColor);
  1517. }
  1518. continue;
  1519. }
  1520. }
  1521. break;
  1522. } while (TRUE);
  1523. #endif
  1524. //
  1525. // No previously zeroed page with the specified secondary color exists.
  1526. // Try a zeroed page of any color.
  1527. //
  1528. Page = MmZeroedPageListHead.Flink;
  1529. if (Page != MM_EMPTY_LIST) {
  1530. #if DBG
  1531. Pfn1 = MI_PFN_ELEMENT(Page);
  1532. #endif
  1533. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1534. ASSERT (Pfn1->u3.e1.PageLocation == ZeroedPageList);
  1535. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1536. Color = MI_GET_COLOR_FROM_LIST_ENTRY(Page, MI_PFN_ELEMENT(Page));
  1537. Page = MiRemovePageByColor (Page, Color);
  1538. ASSERT (Pfn1 == MI_PFN_ELEMENT(Page));
  1539. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1540. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  1541. ASSERT (Pfn1->u2.ShareCount == 0);
  1542. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1543. return Page;
  1544. }
  1545. //
  1546. // No zeroed page of the primary color exists, try a free page of the
  1547. // secondary color. Note in the multinode case this has already been done
  1548. // above.
  1549. //
  1550. #if defined(MI_MULTINODE)
  1551. if (KeNumberNodes <= 1) {
  1552. #endif
  1553. FreePagesByColor = MmFreePagesByColor[FreePageList];
  1554. Page = FreePagesByColor[Color].Flink;
  1555. if (Page != MM_EMPTY_LIST) {
  1556. //
  1557. // Remove the first entry on the free list by color.
  1558. //
  1559. #if DBG
  1560. Pfn1 = MI_PFN_ELEMENT(Page);
  1561. #endif
  1562. ASSERT (Pfn1->u3.e1.PageLocation == FreePageList);
  1563. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1564. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1565. Page = MiRemovePageByColor (Page, Color);
  1566. ASSERT (Pfn1 == MI_PFN_ELEMENT(Page));
  1567. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1568. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  1569. ASSERT (Pfn1->u2.ShareCount == 0);
  1570. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1571. goto ZeroPage;
  1572. }
  1573. #if defined(MI_MULTINODE)
  1574. }
  1575. #endif
  1576. Page = MmFreePageListHead.Flink;
  1577. if (Page != MM_EMPTY_LIST) {
  1578. Color = MI_GET_COLOR_FROM_LIST_ENTRY(Page, MI_PFN_ELEMENT(Page));
  1579. #if DBG
  1580. Pfn1 = MI_PFN_ELEMENT(Page);
  1581. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1582. #endif
  1583. Page = MiRemovePageByColor (Page, Color);
  1584. ASSERT (Pfn1 == MI_PFN_ELEMENT(Page));
  1585. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1586. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  1587. ASSERT (Pfn1->u2.ShareCount == 0);
  1588. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1589. goto ZeroPage;
  1590. }
  1591. ASSERT (MmZeroedPageListHead.Total == 0);
  1592. ASSERT (MmFreePageListHead.Total == 0);
  1593. //
  1594. // Remove a page from the standby list and restore the original
  1595. // contents of the PTE to free the last reference to the physical
  1596. // page.
  1597. //
  1598. ASSERT (MmStandbyPageListHead.Total != 0);
  1599. Page = MiRemovePageFromList (&MmStandbyPageListHead);
  1600. ASSERT ((MI_PFN_ELEMENT(Page))->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1601. ASSERT (MI_PFN_ELEMENT(Page)->u3.e1.CacheAttribute == MiNotMapped);
  1602. MiStandbyRemoved += 1;
  1603. //
  1604. // If memory mirroring is in progress, any removals from the
  1605. // free, zeroed, standby, modified or modified-no-write lists that
  1606. // isn't immediately re-inserting into one of these 5 lists (WITHOUT
  1607. // modifying the page contents) must update the bitmap.
  1608. //
  1609. if (MiMirroringActive == TRUE) {
  1610. RtlSetBit (MiMirrorBitMap2, (ULONG)Page);
  1611. }
  1612. //
  1613. // Zero the page removed from the free or standby list.
  1614. //
  1615. ZeroPage:
  1616. Pfn1 = MI_PFN_ELEMENT(Page);
  1617. MiZeroPhysicalPage (Page, 0);
  1618. #if MI_BARRIER_SUPPORTED
  1619. //
  1620. // Note the stamping must occur after the page is zeroed.
  1621. //
  1622. MI_BARRIER_STAMP_ZEROED_PAGE (&BarrierStamp);
  1623. Pfn1->u4.PteFrame = BarrierStamp;
  1624. #endif
  1625. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1626. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  1627. ASSERT (Pfn1->u2.ShareCount == 0);
  1628. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1629. return Page;
  1630. }
  1631. PFN_NUMBER
  1632. FASTCALL
  1633. MiRemoveAnyPage (
  1634. IN ULONG Color
  1635. )
  1636. /*++
  1637. Routine Description:
  1638. This procedure removes a page from either the free, zeroed,
  1639. or standby lists (in that order). If no pages exist on the zeroed
  1640. or free list a transition page is removed from the standby list
  1641. and the PTE (may be a prototype PTE) which refers to this page is
  1642. changed from transition back to its original contents.
  1643. Note pages MUST exist to satisfy this request. The caller ensures this
  1644. by first calling MiEnsureAvailablePageOrWait.
  1645. Arguments:
  1646. Color - Supplies the page color for which this page is destined.
  1647. This is used for checking virtual address alignments to
  1648. determine if the D cache needs flushing before the page
  1649. can be reused.
  1650. The above was true when we were concerned about caches
  1651. which are virtually indexed. (eg MIPS). Today we
  1652. are more concerned that we get a good usage spread across
  1653. the L2 caches of most machines. These caches are physically
  1654. indexed. By gathering pages that would have the same
  1655. index to the same color, then maximizing the color spread,
  1656. we maximize the effective use of the caches.
  1657. This has been extended for NUMA machines. The high part
  1658. of the color gives the node color (basically node number).
  1659. If we cannot allocate a page of the requested color, we
  1660. try to allocate a page on the same node before taking a
  1661. page from a different node.
  1662. Return Value:
  1663. The physical page number removed from the specified list.
  1664. Environment:
  1665. Must be holding the PFN database lock with APCs disabled.
  1666. --*/
  1667. {
  1668. PFN_NUMBER Page;
  1669. #if DBG
  1670. PMMPFN Pfn1;
  1671. #endif
  1672. #if defined(MI_MULTINODE)
  1673. PKNODE Node;
  1674. ULONG NodeColor;
  1675. ULONG OriginalColor;
  1676. PFN_NUMBER LocalNodePagesAvailable;
  1677. #endif
  1678. MM_PFN_LOCK_ASSERT();
  1679. ASSERT(MmAvailablePages != 0);
  1680. #if defined(MI_MULTINODE)
  1681. //
  1682. // Bias color to memory node. The assumption is that if memory
  1683. // of the correct color is not available on this node, it is
  1684. // better to choose memory of a different color if you can stay
  1685. // on this node.
  1686. //
  1687. LocalNodePagesAvailable = 0;
  1688. NodeColor = Color & ~MmSecondaryColorMask;
  1689. OriginalColor = Color;
  1690. if (KeNumberNodes > 1) {
  1691. Node = MI_NODE_FROM_COLOR(Color);
  1692. LocalNodePagesAvailable = (Node->FreeCount[ZeroedPageList] | Node->FreeCount[ZeroedPageList]);
  1693. }
  1694. do {
  1695. #endif
  1696. //
  1697. // Check the free page list, and if a page is available
  1698. // remove it and return its value.
  1699. //
  1700. ASSERT (Color < MmSecondaryColors);
  1701. if (MmFreePagesByColor[FreePageList][Color].Flink != MM_EMPTY_LIST) {
  1702. //
  1703. // Remove the first entry on the free by color list.
  1704. //
  1705. Page = MmFreePagesByColor[FreePageList][Color].Flink;
  1706. #if DBG
  1707. Pfn1 = MI_PFN_ELEMENT(Page);
  1708. #endif
  1709. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1710. ASSERT (Pfn1->u3.e1.PageLocation == FreePageList);
  1711. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1712. Page = MiRemovePageByColor (Page, Color);
  1713. ASSERT (Pfn1 == MI_PFN_ELEMENT(Page));
  1714. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1715. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  1716. ASSERT (Pfn1->u2.ShareCount == 0);
  1717. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1718. return Page;
  1719. }
  1720. //
  1721. // Try the zero page list by primary color.
  1722. //
  1723. if (MmFreePagesByColor[ZeroedPageList][Color].Flink
  1724. != MM_EMPTY_LIST) {
  1725. //
  1726. // Remove the first entry on the zeroed by color list.
  1727. //
  1728. Page = MmFreePagesByColor[ZeroedPageList][Color].Flink;
  1729. #if DBG
  1730. Pfn1 = MI_PFN_ELEMENT(Page);
  1731. #endif
  1732. ASSERT (Pfn1->u3.e1.PageLocation == ZeroedPageList);
  1733. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1734. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1735. Page = MiRemovePageByColor (Page, Color);
  1736. ASSERT (Pfn1 == MI_PFN_ELEMENT(Page));
  1737. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1738. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1739. return Page;
  1740. }
  1741. //
  1742. // If this is a multinode machine and there are free
  1743. // pages on this node, select another color on this
  1744. // node in preference to random selection.
  1745. //
  1746. #if defined(MI_MULTINODE)
  1747. if (LocalNodePagesAvailable != 0) {
  1748. Color = ((Color + 1) & MmSecondaryColorMask) | NodeColor;
  1749. ASSERT(Color != OriginalColor);
  1750. continue;
  1751. }
  1752. break;
  1753. } while (TRUE);
  1754. #endif
  1755. //
  1756. // Check the free page list, and if a page is available
  1757. // remove it and return its value.
  1758. //
  1759. if (MmFreePageListHead.Flink != MM_EMPTY_LIST) {
  1760. Page = MmFreePageListHead.Flink;
  1761. Color = MI_GET_COLOR_FROM_LIST_ENTRY(Page, MI_PFN_ELEMENT(Page));
  1762. #if DBG
  1763. Pfn1 = MI_PFN_ELEMENT(Page);
  1764. #endif
  1765. ASSERT (Pfn1->u3.e1.PageLocation == FreePageList);
  1766. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1767. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1768. Page = MiRemovePageByColor (Page, Color);
  1769. ASSERT (Pfn1 == MI_PFN_ELEMENT(Page));
  1770. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  1771. ASSERT (Pfn1->u2.ShareCount == 0);
  1772. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1773. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1774. return Page;
  1775. }
  1776. ASSERT (MmFreePageListHead.Total == 0);
  1777. //
  1778. // Check the zeroed page list, and if a page is available
  1779. // remove it and return its value.
  1780. //
  1781. if (MmZeroedPageListHead.Flink != MM_EMPTY_LIST) {
  1782. Page = MmZeroedPageListHead.Flink;
  1783. Color = MI_GET_COLOR_FROM_LIST_ENTRY(Page, MI_PFN_ELEMENT(Page));
  1784. #if DBG
  1785. Pfn1 = MI_PFN_ELEMENT(Page);
  1786. #endif
  1787. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1788. Page = MiRemovePageByColor (Page, Color);
  1789. ASSERT (Pfn1 == MI_PFN_ELEMENT(Page));
  1790. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  1791. ASSERT (Pfn1->u2.ShareCount == 0);
  1792. ASSERT (Pfn1->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1793. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1794. return Page;
  1795. }
  1796. ASSERT (MmZeroedPageListHead.Total == 0);
  1797. //
  1798. // No pages exist on the free or zeroed list, use the standby list.
  1799. //
  1800. ASSERT(MmStandbyPageListHead.Total != 0);
  1801. Page = MiRemovePageFromList (&MmStandbyPageListHead);
  1802. ASSERT ((MI_PFN_ELEMENT(Page))->u4.PteFrame != MI_MAGIC_AWE_PTEFRAME);
  1803. ASSERT ((MI_PFN_ELEMENT(Page))->u3.e1.CacheAttribute == MiNotMapped);
  1804. MiStandbyRemoved += 1;
  1805. //
  1806. // If memory mirroring is in progress, any removals from the
  1807. // free, zeroed, standby, modified or modified-no-write lists that
  1808. // isn't immediately re-inserting into one of these 5 lists (WITHOUT
  1809. // modifying the page contents) must update the bitmap.
  1810. //
  1811. if (MiMirroringActive == TRUE) {
  1812. RtlSetBit (MiMirrorBitMap2, (ULONG)Page);
  1813. }
  1814. MI_CHECK_PAGE_ALIGNMENT(Page, Color & MM_COLOR_MASK);
  1815. #if DBG
  1816. Pfn1 = MI_PFN_ELEMENT (Page);
  1817. #endif
  1818. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  1819. ASSERT (Pfn1->u2.ShareCount == 0);
  1820. return Page;
  1821. }
  1822. PFN_NUMBER
  1823. FASTCALL
  1824. MiRemovePageByColor (
  1825. IN PFN_NUMBER Page,
  1826. IN ULONG Color
  1827. )
  1828. /*++
  1829. Routine Description:
  1830. This procedure removes a page from the middle of the free or
  1831. zeroed page list.
  1832. Arguments:
  1833. Page - Supplies the physical page number to unlink from the list.
  1834. Color - Supplies the page color for which this page is destined.
  1835. This is used for checking virtual address alignments to
  1836. determine if the D cache needs flushing before the page
  1837. can be reused.
  1838. Return Value:
  1839. The page frame number that was unlinked (always equal to the one
  1840. passed in, but returned so the caller's fastcall sequences save
  1841. extra register pushes and pops.
  1842. Environment:
  1843. Must be holding the PFN database lock with APCs disabled.
  1844. --*/
  1845. {
  1846. PMMPFNLIST ListHead;
  1847. PMMPFNLIST PrimaryListHead;
  1848. PFN_NUMBER Previous;
  1849. PFN_NUMBER Next;
  1850. PMMPFN Pfn1;
  1851. PMMPFN Pfn2;
  1852. ULONG NodeColor;
  1853. MMLISTS ListName;
  1854. PMMCOLOR_TABLES ColorHead;
  1855. MM_PFN_LOCK_ASSERT();
  1856. Pfn1 = MI_PFN_ELEMENT (Page);
  1857. NodeColor = Pfn1->u3.e1.PageColor;
  1858. #if defined(MI_MULTINODE)
  1859. ASSERT (NodeColor == (Color >> MmSecondaryColorNodeShift));
  1860. #endif
  1861. if (PERFINFO_IS_GROUP_ON(PERF_MEMORY)) {
  1862. MiLogPfnInformation (Pfn1, PERFINFO_LOG_TYPE_REMOVEPAGEBYCOLOR);
  1863. }
  1864. //
  1865. // If memory mirroring is in progress, any additions or removals to the
  1866. // free, zeroed, standby, modified or modified-no-write lists must
  1867. // update the bitmap.
  1868. //
  1869. if (MiMirroringActive == TRUE) {
  1870. RtlSetBit (MiMirrorBitMap2, (ULONG)Page);
  1871. }
  1872. ListHead = MmPageLocationList[Pfn1->u3.e1.PageLocation];
  1873. ListName = ListHead->ListName;
  1874. ListHead->Total -= 1;
  1875. PrimaryListHead = ListHead;
  1876. Next = Pfn1->u1.Flink;
  1877. Pfn1->u1.Flink = 0; // Assumes Flink width is >= WsIndex width
  1878. Previous = Pfn1->u2.Blink;
  1879. Pfn1->u2.Blink = 0;
  1880. if (Next == MM_EMPTY_LIST) {
  1881. PrimaryListHead->Blink = Previous;
  1882. }
  1883. else {
  1884. Pfn2 = MI_PFN_ELEMENT(Next);
  1885. Pfn2->u2.Blink = Previous;
  1886. }
  1887. if (Previous == MM_EMPTY_LIST) {
  1888. PrimaryListHead->Flink = Next;
  1889. }
  1890. else {
  1891. Pfn2 = MI_PFN_ELEMENT(Previous);
  1892. Pfn2->u1.Flink = Next;
  1893. }
  1894. ASSERT (Pfn1->u3.e1.RemovalRequested == 0);
  1895. //
  1896. // Zero the flags longword, but keep the color and cache information.
  1897. //
  1898. ASSERT (Pfn1->u3.e1.CacheAttribute == MiNotMapped);
  1899. ASSERT (Pfn1->u3.e1.Rom == 0);
  1900. Pfn1->u3.e2.ShortFlags = 0;
  1901. Pfn1->u3.e1.PageColor = NodeColor;
  1902. Pfn1->u3.e1.CacheAttribute = MiNotMapped;
  1903. //
  1904. // Update the color lists.
  1905. //
  1906. ASSERT (Color < MmSecondaryColors);
  1907. ColorHead = &MmFreePagesByColor[ListName][Color];
  1908. ColorHead->Flink = (PFN_NUMBER) Pfn1->OriginalPte.u.Long;
  1909. ASSERT (ColorHead->Count >= 1);
  1910. ColorHead->Count -= 1;
  1911. //
  1912. // Note that we now have one less page available.
  1913. //
  1914. #if defined(MI_MULTINODE)
  1915. if (KeNumberNodes > 1) {
  1916. KeNodeBlock[NodeColor]->FreeCount[ListName]--;
  1917. }
  1918. #endif
  1919. MmAvailablePages -= 1;
  1920. if (MmAvailablePages < MmMinimumFreePages) {
  1921. //
  1922. // Obtain free pages.
  1923. //
  1924. MiObtainFreePages();
  1925. }
  1926. return Page;
  1927. }
  1928. VOID
  1929. FASTCALL
  1930. MiInsertFrontModifiedNoWrite (
  1931. IN PFN_NUMBER PageFrameIndex
  1932. )
  1933. /*++
  1934. Routine Description:
  1935. This procedure inserts a page at the FRONT of the modified no write list.
  1936. Arguments:
  1937. PageFrameIndex - Supplies the physical page number to insert in the list.
  1938. Return Value:
  1939. None.
  1940. Environment:
  1941. Must be holding the PFN database lock with APCs disabled.
  1942. --*/
  1943. {
  1944. PFN_NUMBER first;
  1945. PMMPFN Pfn1;
  1946. PMMPFN Pfn2;
  1947. MM_PFN_LOCK_ASSERT();
  1948. ASSERT ((PageFrameIndex != 0) && (PageFrameIndex <= MmHighestPhysicalPage) &&
  1949. (PageFrameIndex >= MmLowestPhysicalPage));
  1950. //
  1951. // Check to ensure the reference count for the page is zero.
  1952. //
  1953. Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
  1954. MI_SNAP_DATA (Pfn1, Pfn1->PteAddress, 0xA);
  1955. ASSERT (Pfn1->u3.e1.CacheAttribute == MiCached);
  1956. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  1957. MmModifiedNoWritePageListHead.Total += 1; // One more page on the list.
  1958. MI_TALLY_TRANSITION_PAGE_ADDITION (Pfn1);
  1959. first = MmModifiedNoWritePageListHead.Flink;
  1960. if (first == MM_EMPTY_LIST) {
  1961. //
  1962. // List is empty add the page to the ListHead.
  1963. //
  1964. MmModifiedNoWritePageListHead.Blink = PageFrameIndex;
  1965. }
  1966. else {
  1967. Pfn2 = MI_PFN_ELEMENT (first);
  1968. Pfn2->u2.Blink = PageFrameIndex;
  1969. }
  1970. MmModifiedNoWritePageListHead.Flink = PageFrameIndex;
  1971. Pfn1->u1.Flink = first;
  1972. Pfn1->u2.Blink = MM_EMPTY_LIST;
  1973. Pfn1->u3.e1.PageLocation = ModifiedNoWritePageList;
  1974. return;
  1975. }
  1976. PFN_NUMBER
  1977. MiAllocatePfn (
  1978. IN PMMPTE PointerPte,
  1979. IN ULONG Protection
  1980. )
  1981. /*++
  1982. Routine Description:
  1983. This procedure allocates and initializes a page of memory.
  1984. Arguments:
  1985. PointerPte - Supplies the PTE to initialize.
  1986. Return Value:
  1987. The page frame index allocated.
  1988. Environment:
  1989. Kernel mode.
  1990. --*/
  1991. {
  1992. KIRQL OldIrql;
  1993. PFN_NUMBER PageFrameIndex;
  1994. LOCK_PFN (OldIrql);
  1995. MiEnsureAvailablePageOrWait (NULL, NULL);
  1996. PageFrameIndex = MiRemoveAnyPage (MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));
  1997. PointerPte->u.Long = MM_KERNEL_DEMAND_ZERO_PTE;
  1998. PointerPte->u.Soft.Protection |= Protection;
  1999. MiInitializePfn (PageFrameIndex, PointerPte, 1);
  2000. UNLOCK_PFN (OldIrql);
  2001. return PageFrameIndex;
  2002. }
  2003. VOID
  2004. FASTCALL
  2005. MiLogPfnInformation (
  2006. IN PMMPFN Pfn1,
  2007. IN USHORT Reason
  2008. )
  2009. {
  2010. MMPFN_IDENTITY PfnIdentity;
  2011. RtlZeroMemory (&PfnIdentity, sizeof(PfnIdentity));
  2012. if (Reason == PERFINFO_LOG_TYPE_INSERTINFREELIST) {
  2013. MI_MARK_PFN_UNDELETED (Pfn1);
  2014. }
  2015. MiIdentifyPfn(Pfn1, &PfnIdentity);
  2016. PerfInfoLogBytes (Reason, &PfnIdentity, sizeof(PfnIdentity));
  2017. if (Reason == PERFINFO_LOG_TYPE_INSERTINFREELIST) {
  2018. MI_SET_PFN_DELETED (Pfn1);
  2019. }
  2020. }
  2021. VOID
  2022. MiPurgeTransitionList (
  2023. VOID
  2024. )
  2025. {
  2026. PMMPFN Pfn1;
  2027. KIRQL OldIrql;
  2028. PFN_NUMBER PageFrameIndex;
  2029. //
  2030. // Run the transition list and free all the entries so transition
  2031. // faults are not satisfied for any of the non modified pages that were
  2032. // freed.
  2033. //
  2034. LOCK_PFN (OldIrql);
  2035. while (MmStandbyPageListHead.Total != 0) {
  2036. PageFrameIndex = MiRemovePageFromList (&MmStandbyPageListHead);
  2037. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  2038. ASSERT (Pfn1->u2.ShareCount == 0);
  2039. ASSERT (Pfn1->u3.e2.ReferenceCount == 0);
  2040. Pfn1->u3.e2.ReferenceCount += 1;
  2041. Pfn1->OriginalPte = ZeroPte;
  2042. MI_SET_PFN_DELETED (Pfn1);
  2043. MiDecrementReferenceCount (PageFrameIndex);
  2044. //
  2045. // If memory mirroring is in progress, any removal from
  2046. // the standby, modified or modified-no-write lists that isn't
  2047. // immediately re-inserting in one of these 3 lists must
  2048. // update the bitmap.
  2049. //
  2050. if (MiMirroringActive == TRUE) {
  2051. RtlSetBit (MiMirrorBitMap2, (ULONG)PageFrameIndex);
  2052. }
  2053. }
  2054. UNLOCK_PFN (OldIrql);
  2055. return;
  2056. }