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.

396 lines
12 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 PMMPFN Pfn1,
  20. IN PFN_NUMBER PageFrameIndex
  21. )
  22. /*++
  23. Routine Description:
  24. This routine decrements the share count within the PFN element
  25. for the specified physical page. If the share count becomes
  26. zero the corresponding PTE is converted to the transition state
  27. and the reference count is decremented and the ValidPte count
  28. of the PTEframe is decremented.
  29. Arguments:
  30. Pfn1 - Supplies the PFN database entry to decrement.
  31. PageFrameIndex - Supplies the physical page number of which to decrement
  32. the share count.
  33. Return Value:
  34. None.
  35. Environment:
  36. Must be holding the PFN database lock with APCs disabled.
  37. --*/
  38. {
  39. ULONG FreeBit;
  40. MMPTE TempPte;
  41. PMMPTE PointerPte;
  42. PEPROCESS Process;
  43. ASSERT ((PageFrameIndex <= MmHighestPhysicalPage) &&
  44. (PageFrameIndex > 0));
  45. ASSERT (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 (MiIsAddressValid (Pfn1->PteAddress, TRUE)) {
  78. Process = NULL;
  79. PointerPte = Pfn1->PteAddress;
  80. }
  81. else {
  82. //
  83. // The address is not valid in this process, map it into
  84. // hyperspace so it can be operated upon.
  85. //
  86. Process = PsGetCurrentProcess ();
  87. PointerPte = (PMMPTE)MiMapPageInHyperSpaceAtDpc(Process, Pfn1->u4.PteFrame);
  88. PointerPte = (PMMPTE)((PCHAR)PointerPte +
  89. MiGetByteOffset(Pfn1->PteAddress));
  90. }
  91. TempPte = *PointerPte;
  92. MI_MAKE_VALID_PTE_TRANSITION (TempPte,
  93. Pfn1->OriginalPte.u.Soft.Protection);
  94. MI_WRITE_INVALID_PTE (PointerPte, TempPte);
  95. if (Process != NULL) {
  96. MiUnmapPageInHyperSpaceFromDpc (Process, PointerPte);
  97. }
  98. //
  99. // There is no need to flush the translation buffer at this
  100. // time as we only invalidated a prototype PTE.
  101. //
  102. }
  103. //
  104. // Change the page location to inactive (from active and valid).
  105. //
  106. Pfn1->u3.e1.PageLocation = TransitionPage;
  107. //
  108. // Decrement the reference count as the share count is now zero.
  109. //
  110. MM_PFN_LOCK_ASSERT();
  111. ASSERT (Pfn1->u3.e2.ReferenceCount != 0);
  112. if (Pfn1->u3.e2.ReferenceCount == 1) {
  113. if (MI_IS_PFN_DELETED (Pfn1)) {
  114. Pfn1->u3.e2.ReferenceCount = 0;
  115. //
  116. // There is no referenced PTE for this page, delete the page
  117. // file space (if any), and place the page on the free list.
  118. //
  119. if ((Pfn1->u3.e1.CacheAttribute != MiCached) &&
  120. (Pfn1->u3.e1.CacheAttribute != MiNotMapped)) {
  121. //
  122. // This page was mapped as noncached or writecombined, and
  123. // is now being freed. There may be a mapping for this
  124. // page still in the TB because during system PTE unmap,
  125. // the PTEs are zeroed but the TB is not flushed (in the
  126. // interest of best performance).
  127. //
  128. // Flushing the TB on a per-page basis is admittedly
  129. // expensive, especially in MP machines and if multiple
  130. // pages are being done this way instead of batching them,
  131. // but this should be a fairly rare occurrence.
  132. //
  133. // The TB must be flushed to ensure no stale mapping
  134. // resides in it before this page can be given out with
  135. // a conflicting mapping (ie: cached). Since it's going
  136. // on the freelist now, this must be completed before the
  137. // PFN lock is released.
  138. //
  139. // A more elaborate scheme similar to the timestamping
  140. // wrt to zeroing pages could be added if this becomes
  141. // a hot path.
  142. //
  143. MiFlushForNonCached += 1;
  144. KeFlushEntireTb (TRUE, TRUE);
  145. }
  146. ASSERT (Pfn1->OriginalPte.u.Soft.Prototype == 0);
  147. FreeBit = GET_PAGING_FILE_OFFSET (Pfn1->OriginalPte);
  148. if ((FreeBit != 0) && (FreeBit != MI_PTE_LOOKUP_NEEDED)) {
  149. MiReleaseConfirmedPageFileSpace (Pfn1->OriginalPte);
  150. }
  151. //
  152. // Temporarily mark the frame as active and valid so that
  153. // MiIdentifyPfn knows it is safe to walk back through the
  154. // containing frames for a more accurate identification.
  155. // Note the page will be immediately re-marked as it is
  156. // inserted into the freelist.
  157. //
  158. Pfn1->u3.e1.PageLocation = ActiveAndValid;
  159. MiInsertPageInFreeList (PageFrameIndex);
  160. }
  161. else {
  162. MiDecrementReferenceCount (Pfn1, PageFrameIndex);
  163. }
  164. }
  165. else {
  166. Pfn1->u3.e2.ReferenceCount -= 1;
  167. }
  168. }
  169. return;
  170. }
  171. VOID
  172. FASTCALL
  173. MiDecrementReferenceCount (
  174. IN PMMPFN Pfn1,
  175. IN PFN_NUMBER PageFrameIndex
  176. )
  177. /*++
  178. Routine Description:
  179. This routine decrements the reference count for the specified page.
  180. If the reference count becomes zero, the page is placed on the
  181. appropriate list (free, modified, standby or bad). If the page
  182. is placed on the free or standby list, the number of available
  183. pages is incremented and if it transitions from zero to one, the
  184. available page event is set.
  185. Arguments:
  186. Pfn1 - Supplies the PFN database entry to decrement.
  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. ULONG FreeBit;
  196. MM_PFN_LOCK_ASSERT();
  197. ASSERT (PageFrameIndex <= MmHighestPhysicalPage);
  198. ASSERT (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
  222. // file 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
  228. // is now being freed. There may be a mapping for this
  229. // page still in the TB because during system PTE unmap,
  230. // the PTEs are zeroed but the TB is not flushed (in the
  231. // interest of best performance).
  232. //
  233. // Flushing the TB on a per-page basis is admittedly
  234. // expensive, especially in MP machines and if multiple
  235. // pages are being done this way instead of batching them,
  236. // but this should be a fairly rare occurrence.
  237. //
  238. // The TB must be flushed to ensure no stale mapping
  239. // resides in it before this page can be given out with
  240. // a conflicting mapping (ie: cached). Since it's going
  241. // on the freelist now, this must be completed before the
  242. // PFN lock is released.
  243. //
  244. // A more elaborate scheme similar to the timestamping
  245. // wrt to zeroing pages could be added if this becomes
  246. // a hot path.
  247. //
  248. MiFlushForNonCached += 1;
  249. KeFlushEntireTb (TRUE, TRUE);
  250. }
  251. if (Pfn1->OriginalPte.u.Soft.Prototype == 0) {
  252. FreeBit = GET_PAGING_FILE_OFFSET (Pfn1->OriginalPte);
  253. if ((FreeBit != 0) && (FreeBit != MI_PTE_LOOKUP_NEEDED)) {
  254. MiReleaseConfirmedPageFileSpace (Pfn1->OriginalPte);
  255. }
  256. }
  257. MiInsertPageInFreeList (PageFrameIndex);
  258. return;
  259. }
  260. ASSERT ((Pfn1->u3.e1.CacheAttribute != MiNonCached) &&
  261. (Pfn1->u3.e1.CacheAttribute != MiWriteCombined));
  262. //
  263. // Place the page on the modified or standby list depending
  264. // on the state of the modify bit in the PFN element.
  265. //
  266. if (Pfn1->u3.e1.Modified == 1) {
  267. MiInsertPageInList (&MmModifiedPageListHead, PageFrameIndex);
  268. }
  269. else {
  270. if (Pfn1->u3.e1.RemovalRequested == 1) {
  271. //
  272. // The page may still be marked as on the modified list if the
  273. // current thread is the modified writer completing the write.
  274. // Mark it as standby so restoration of the transition PTE
  275. // doesn't flag this as illegal.
  276. //
  277. Pfn1->u3.e1.PageLocation = StandbyPageList;
  278. MiRestoreTransitionPte (Pfn1);
  279. MiInsertPageInList (&MmBadPageListHead, PageFrameIndex);
  280. return;
  281. }
  282. if (!MmFrontOfList) {
  283. MiInsertPageInList (&MmStandbyPageListHead, PageFrameIndex);
  284. }
  285. else {
  286. MiInsertStandbyListAtFront (PageFrameIndex);
  287. }
  288. }
  289. return;
  290. }