Leaked source code of windows server 2003
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.

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