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.

384 lines
11 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. pfndec.c
  5. Abstract:
  6. This module contains the routines to decrement the share count and
  7. the reference counts within the Page Frame Database.
  8. Author:
  9. Lou Perazzoli (loup) 5-Apr-1989
  10. Landy Wang (landyw) 2-Jun-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. ULONG MmFrontOfList;
  15. ULONG MiFlushForNonCached;
  16. VOID
  17. FASTCALL
  18. MiDecrementShareCount (
  19. IN PFN_NUMBER PageFrameIndex
  20. )
  21. /*++
  22. Routine Description:
  23. This routine decrements the share count within the PFN element
  24. for the specified physical page. If the share count becomes
  25. zero the corresponding PTE is converted to the transition state
  26. and the reference count is decremented and the ValidPte count
  27. of the PTEframe is decremented.
  28. Arguments:
  29. PageFrameIndex - Supplies the physical page number of which to decrement
  30. the share count.
  31. Return Value:
  32. None.
  33. Environment:
  34. Must be holding the PFN database lock with APCs disabled.
  35. --*/
  36. {
  37. ULONG FreeBit;
  38. MMPTE TempPte;
  39. PMMPTE PointerPte;
  40. PMMPFN Pfn1;
  41. LOGICAL HyperMapped;
  42. PEPROCESS Process;
  43. ASSERT ((PageFrameIndex <= MmHighestPhysicalPage) &&
  44. (PageFrameIndex > 0));
  45. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  46. if (Pfn1->u3.e1.PageLocation != ActiveAndValid &&
  47. Pfn1->u3.e1.PageLocation != StandbyPageList) {
  48. KeBugCheckEx (PFN_LIST_CORRUPT,
  49. 0x99,
  50. PageFrameIndex,
  51. Pfn1->u3.e1.PageLocation,
  52. 0);
  53. }
  54. Pfn1->u2.ShareCount -= 1;
  55. PERFINFO_DECREFCNT(Pfn1, PERF_SOFT_TRIM, PERFINFO_LOG_TYPE_DECSHARCNT);
  56. ASSERT (Pfn1->u2.ShareCount < 0xF000000);
  57. if (Pfn1->u2.ShareCount == 0) {
  58. if (PERFINFO_IS_GROUP_ON(PERF_MEMORY)) {
  59. PERFINFO_PFN_INFORMATION PerfInfoPfn;
  60. PerfInfoPfn.PageFrameIndex = PageFrameIndex;
  61. PerfInfoLogBytes(PERFINFO_LOG_TYPE_ZEROSHARECOUNT, &PerfInfoPfn, sizeof(PerfInfoPfn));
  62. }
  63. //
  64. // The share count is now zero, decrement the reference count
  65. // for the PFN element and turn the referenced PTE into
  66. // the transition state if it refers to a prototype PTE.
  67. // PTEs which are not prototype PTEs do not need to be placed
  68. // into transition as they are placed in transition when
  69. // they are removed from the working set (working set free routine).
  70. //
  71. //
  72. // If the PTE referenced by this PFN element is actually
  73. // a prototype PTE, it must be mapped into hyperspace and
  74. // then operated on.
  75. //
  76. if (Pfn1->u3.e1.PrototypePte == 1) {
  77. if (MmIsAddressValid (Pfn1->PteAddress)) {
  78. Process = NULL;
  79. PointerPte = Pfn1->PteAddress;
  80. HyperMapped = FALSE;
  81. }
  82. else {
  83. //
  84. // The address is not valid in this process, map it into
  85. // hyperspace so it can be operated upon.
  86. //
  87. HyperMapped = TRUE;
  88. Process = PsGetCurrentProcess ();
  89. PointerPte = (PMMPTE)MiMapPageInHyperSpaceAtDpc(Process, Pfn1->u4.PteFrame);
  90. PointerPte = (PMMPTE)((PCHAR)PointerPte +
  91. MiGetByteOffset(Pfn1->PteAddress));
  92. }
  93. TempPte = *PointerPte;
  94. MI_MAKE_VALID_PTE_TRANSITION (TempPte,
  95. Pfn1->OriginalPte.u.Soft.Protection);
  96. MI_WRITE_INVALID_PTE (PointerPte, TempPte);
  97. if (HyperMapped == TRUE) {
  98. MiUnmapPageInHyperSpaceFromDpc (Process, PointerPte);
  99. }
  100. //
  101. // There is no need to flush the translation buffer at this
  102. // time as we only invalidated a prototype PTE.
  103. //
  104. }
  105. //
  106. // Change the page location to inactive (from active and valid).
  107. //
  108. Pfn1->u3.e1.PageLocation = TransitionPage;
  109. //
  110. // Decrement the reference count as the share count is now zero.
  111. //
  112. MM_PFN_LOCK_ASSERT();
  113. ASSERT (Pfn1->u3.e2.ReferenceCount != 0);
  114. if (Pfn1->u3.e2.ReferenceCount == 1) {
  115. if (MI_IS_PFN_DELETED (Pfn1)) {
  116. Pfn1->u3.e2.ReferenceCount = 0;
  117. //
  118. // There is no referenced PTE for this page, delete the page
  119. // file space (if any), and place the page on the free list.
  120. //
  121. if ((Pfn1->u3.e1.CacheAttribute != MiCached) &&
  122. (Pfn1->u3.e1.CacheAttribute != MiNotMapped)) {
  123. //
  124. // This page was mapped as noncached or writecombined, and
  125. // is now being freed. There may be a mapping for this
  126. // page still in the TB because during system PTE unmap,
  127. // the PTEs are zeroed but the TB is not flushed (in the
  128. // interest of best performance).
  129. //
  130. // Flushing the TB on a per-page basis is admittedly
  131. // expensive, especially in MP machines and if multiple
  132. // pages are being done this way instead of batching them,
  133. // but this should be a fairly rare occurrence.
  134. //
  135. // The TB must be flushed to ensure no stale mapping
  136. // resides in it before this page can be given out with
  137. // a conflicting mapping (ie: cached). Since it's going
  138. // on the freelist now, this must be completed before the
  139. // PFN lock is released.
  140. //
  141. // A more elaborate scheme similar to the timestamping
  142. // wrt to zeroing pages could be added if this becomes
  143. // a hot path.
  144. //
  145. MiFlushForNonCached += 1;
  146. KeFlushEntireTb (TRUE, TRUE);
  147. }
  148. ASSERT (Pfn1->OriginalPte.u.Soft.Prototype == 0);
  149. FreeBit = GET_PAGING_FILE_OFFSET (Pfn1->OriginalPte);
  150. if ((FreeBit != 0) && (FreeBit != MI_PTE_LOOKUP_NEEDED)) {
  151. MiReleaseConfirmedPageFileSpace (Pfn1->OriginalPte);
  152. }
  153. //
  154. // Temporarily mark the frame as active and valid so that
  155. // MiIdentifyPfn knows it is safe to walk back through the
  156. // containing frames for a more accurate identification.
  157. // Note the page will be immediately re-marked as it is
  158. // inserted into the freelist.
  159. //
  160. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  161. MiInsertPageInFreeList (PageFrameIndex);
  162. }
  163. else {
  164. MiDecrementReferenceCount (PageFrameIndex);
  165. }
  166. }
  167. else {
  168. Pfn1->u3.e2.ReferenceCount -= 1;
  169. }
  170. }
  171. return;
  172. }
  173. VOID
  174. FASTCALL
  175. MiDecrementReferenceCount (
  176. IN PFN_NUMBER PageFrameIndex
  177. )
  178. /*++
  179. Routine Description:
  180. This routine decrements the reference count for the specified page.
  181. If the reference count becomes zero, the page is placed on the
  182. appropriate list (free, modified, standby or bad). If the page
  183. is placed on the free or standby list, the number of available
  184. pages is incremented and if it transitions from zero to one, the
  185. available page event is set.
  186. Arguments:
  187. PageFrameIndex - Supplies the physical page number of which to
  188. decrement the reference count.
  189. Return Value:
  190. none.
  191. Environment:
  192. Must be holding the PFN database lock with APCs disabled.
  193. --*/
  194. {
  195. PMMPFN Pfn1;
  196. MM_PFN_LOCK_ASSERT();
  197. ASSERT (PageFrameIndex <= MmHighestPhysicalPage);
  198. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  199. ASSERT (Pfn1->u3.e2.ReferenceCount != 0);
  200. Pfn1->u3.e2.ReferenceCount -= 1;
  201. if (Pfn1->u3.e2.ReferenceCount != 0) {
  202. //
  203. // The reference count is not zero, return.
  204. //
  205. return;
  206. }
  207. //
  208. // The reference count is now zero, put the page on some list.
  209. //
  210. if (Pfn1->u2.ShareCount != 0) {
  211. KeBugCheckEx (PFN_LIST_CORRUPT,
  212. 7,
  213. PageFrameIndex,
  214. Pfn1->u2.ShareCount,
  215. 0);
  216. return;
  217. }
  218. ASSERT (Pfn1->u3.e1.PageLocation != ActiveAndValid);
  219. if (MI_IS_PFN_DELETED (Pfn1)) {
  220. //
  221. // There is no referenced PTE for this page, delete the page file
  222. // space (if any), and place the page on the free list.
  223. //
  224. if ((Pfn1->u3.e1.CacheAttribute != MiCached) &&
  225. (Pfn1->u3.e1.CacheAttribute != MiNotMapped)) {
  226. //
  227. // This page was mapped as noncached or writecombined, and is now
  228. // being freed. There may be a mapping for this page still in the
  229. // TB because system PTE unmap, the PTEs are zeroed but the TB
  230. // is not flushed (in the interest of best performance).
  231. //
  232. // Flushing the TB on a per-page basis is admittedly expensive,
  233. // especially in MP machines and if multiple pages are being done
  234. // this way instead of batching them, but this should be a
  235. // fairly rare occurrence.
  236. //
  237. // The TB must be flushed to ensure no stale mapping resides in it
  238. // before this page can be given out with a conflicting mapping
  239. // (ie: cached). Since it's going on the freelist now, this must
  240. // be completed before the PFN lock is released.
  241. //
  242. // A more elaborate scheme similar to the timestamping wrt to
  243. // zeroing pages could be added if this becomes a hot path.
  244. //
  245. MiFlushForNonCached += 1;
  246. KeFlushEntireTb (TRUE, TRUE);
  247. }
  248. MiReleasePageFileSpace (Pfn1->OriginalPte);
  249. MiInsertPageInFreeList (PageFrameIndex);
  250. return;
  251. }
  252. ASSERT ((Pfn1->u3.e1.CacheAttribute != MiNonCached) &&
  253. (Pfn1->u3.e1.CacheAttribute != MiWriteCombined));
  254. //
  255. // Place the page on the modified or standby list depending
  256. // on the state of the modify bit in the PFN element.
  257. //
  258. if (Pfn1->u3.e1.Modified == 1) {
  259. MiInsertPageInList (&MmModifiedPageListHead, PageFrameIndex);
  260. }
  261. else {
  262. if (Pfn1->u3.e1.RemovalRequested == 1) {
  263. //
  264. // The page may still be marked as on the modified list if the
  265. // current thread is the modified writer completing the write.
  266. // Mark it as standby so restoration of the transition PTE
  267. // doesn't flag this as illegal.
  268. //
  269. Pfn1->u3.e1.PageLocation = StandbyPageList;
  270. MiRestoreTransitionPte (PageFrameIndex);
  271. MiInsertPageInList (&MmBadPageListHead, PageFrameIndex);
  272. return;
  273. }
  274. if (!MmFrontOfList) {
  275. MiInsertPageInList (&MmStandbyPageListHead, PageFrameIndex);
  276. }
  277. else {
  278. MiInsertStandbyListAtFront (PageFrameIndex);
  279. }
  280. }
  281. return;
  282. }