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.

371 lines
8.9 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. wrtfault.c
  5. Abstract:
  6. This module contains the copy on write routine for memory management.
  7. Author:
  8. Lou Perazzoli (loup) 10-Apr-1989
  9. Revision History:
  10. --*/
  11. #include "mi.h"
  12. LOGICAL
  13. FASTCALL
  14. MiCopyOnWrite (
  15. IN PVOID FaultingAddress,
  16. IN PMMPTE PointerPte
  17. )
  18. /*++
  19. Routine Description:
  20. This routine performs a copy on write operation for the specified
  21. virtual address.
  22. Arguments:
  23. FaultingAddress - Supplies the virtual address which caused the
  24. fault.
  25. PointerPte - Supplies the pointer to the PTE which caused the
  26. page fault.
  27. Return Value:
  28. Returns TRUE if the page was actually split, FALSE if not.
  29. Environment:
  30. Kernel mode, APCs disabled, working set mutex held.
  31. --*/
  32. {
  33. MMPTE TempPte;
  34. PFN_NUMBER PageFrameIndex;
  35. PFN_NUMBER NewPageIndex;
  36. PULONG CopyTo;
  37. PULONG CopyFrom;
  38. KIRQL OldIrql;
  39. PMMPFN Pfn1;
  40. PEPROCESS CurrentProcess;
  41. PMMCLONE_BLOCK CloneBlock;
  42. PMMCLONE_DESCRIPTOR CloneDescriptor;
  43. PVOID VirtualAddress;
  44. WSLE_NUMBER WorkingSetIndex;
  45. LOGICAL FakeCopyOnWrite;
  46. FakeCopyOnWrite = FALSE;
  47. CurrentProcess = PsGetCurrentProcess ();
  48. //
  49. // This is called from MmAccessFault, the PointerPte is valid
  50. // and the working set mutex ensures it cannot change state.
  51. //
  52. // Capture the PTE contents to TempPte.
  53. //
  54. TempPte = *PointerPte;
  55. //
  56. // Check to see if this is a prototype PTE with copy on write
  57. // enabled.
  58. //
  59. if (TempPte.u.Hard.CopyOnWrite == 0) {
  60. //
  61. // This is a fork page which is being made private in order
  62. // to change the protection of the page.
  63. // Do not make the page writable.
  64. //
  65. FakeCopyOnWrite = TRUE;
  66. }
  67. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte);
  68. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  69. VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  70. WorkingSetIndex = MiLocateWsle (VirtualAddress, MmWorkingSetList,
  71. Pfn1->u1.WsIndex);
  72. //
  73. // The page must be copied into a new page.
  74. //
  75. //
  76. // If a fork operation is in progress, block until the fork is completed,
  77. // then retry the whole operation as the state of everything may have
  78. // changed between when the mutexes were released and reacquired.
  79. //
  80. if (CurrentProcess->ForkInProgress != NULL) {
  81. if (MiWaitForForkToComplete (CurrentProcess, FALSE) == TRUE) {
  82. return FALSE;
  83. }
  84. }
  85. LOCK_PFN (OldIrql);
  86. if (MiEnsureAvailablePageOrWait (CurrentProcess, NULL)) {
  87. //
  88. // A wait operation was performed to obtain an available
  89. // page and the working set mutex and PFN lock have
  90. // been released and various things may have changed for
  91. // the worse. Rather than examine all the conditions again,
  92. // return and if things are still proper, the fault will
  93. // be taken again.
  94. //
  95. UNLOCK_PFN (OldIrql);
  96. return FALSE;
  97. }
  98. //
  99. // Increment the number of private pages.
  100. //
  101. CurrentProcess->NumberOfPrivatePages += 1;
  102. MmInfoCounters.CopyOnWriteCount += 1;
  103. //
  104. // A page is being copied and made private, the global state of
  105. // the shared page needs to be updated at this point on certain
  106. // hardware. This is done by ORing the dirty bit into the modify bit in
  107. // the PFN element.
  108. //
  109. MI_CAPTURE_DIRTY_BIT_TO_PFN (PointerPte, Pfn1);
  110. //
  111. // This must be a prototype PTE. Perform the copy on write.
  112. //
  113. #if DBG
  114. if (Pfn1->u3.e1.PrototypePte == 0) {
  115. DbgPrint("writefault - PTE indicates cow but not protopte\n");
  116. MiFormatPte(PointerPte);
  117. MiFormatPfn(Pfn1);
  118. }
  119. #endif
  120. CloneBlock = (PMMCLONE_BLOCK)Pfn1->PteAddress;
  121. //
  122. // Get a new page with the same color as this page.
  123. //
  124. NewPageIndex = MiRemoveAnyPage (
  125. MI_PAGE_COLOR_PTE_PROCESS(PageFrameIndex,
  126. &CurrentProcess->NextPageColor));
  127. MiInitializeCopyOnWritePfn (NewPageIndex, PointerPte, WorkingSetIndex, NULL);
  128. UNLOCK_PFN (OldIrql);
  129. CopyTo = (PULONG)MiMapPageInHyperSpace (CurrentProcess, NewPageIndex, &OldIrql);
  130. #if defined(_MIALT4K_)
  131. //
  132. // Avoid accessing user space as it may potentially
  133. // cause a page fault on the alternate table.
  134. //
  135. CopyFrom = KSEG_ADDRESS(PointerPte->u.Hard.PageFrameNumber);
  136. #else
  137. CopyFrom = (PULONG)MiGetVirtualAddressMappedByPte (PointerPte);
  138. #endif
  139. RtlCopyMemory ( CopyTo, CopyFrom, PAGE_SIZE);
  140. PERFINFO_PRIVATE_COPY_ON_WRITE(CopyFrom, PAGE_SIZE);
  141. MiUnmapPageInHyperSpace (CurrentProcess, CopyTo, OldIrql);
  142. if (!FakeCopyOnWrite) {
  143. //
  144. // If the page was really a copy on write page, make it
  145. // accessed, dirty and writable. Also, clear the copy-on-write
  146. // bit in the PTE.
  147. //
  148. MI_SET_PTE_DIRTY (TempPte);
  149. TempPte.u.Hard.Write = 1;
  150. MI_SET_ACCESSED_IN_PTE (&TempPte, 1);
  151. TempPte.u.Hard.CopyOnWrite = 0;
  152. TempPte.u.Hard.PageFrameNumber = NewPageIndex;
  153. }
  154. else {
  155. //
  156. // The page was not really a copy on write, just change
  157. // the frame field of the PTE.
  158. //
  159. TempPte.u.Hard.PageFrameNumber = NewPageIndex;
  160. }
  161. //
  162. // If the modify bit is set in the PFN database for the
  163. // page, the data cache must be flushed. This is due to the
  164. // fact that this process may have been cloned and the cache
  165. // still contains stale data destined for the page we are
  166. // going to remove.
  167. //
  168. ASSERT (TempPte.u.Hard.Valid == 1);
  169. LOCK_PFN (OldIrql);
  170. //
  171. // Flush the TB entry for this page.
  172. //
  173. KeFlushSingleTb (FaultingAddress,
  174. TRUE,
  175. FALSE,
  176. (PHARDWARE_PTE)PointerPte,
  177. TempPte.u.Flush);
  178. //
  179. // Decrement the share count for the page which was copied
  180. // as this PTE no longer refers to it.
  181. //
  182. MiDecrementShareCount (PageFrameIndex);
  183. CloneDescriptor = MiLocateCloneAddress (CurrentProcess, (PVOID)CloneBlock);
  184. if (CloneDescriptor != NULL) {
  185. //
  186. // Decrement the reference count for the clone block,
  187. // note that this could release and reacquire the mutexes.
  188. //
  189. MiDecrementCloneBlockReference (CloneDescriptor,
  190. CloneBlock,
  191. CurrentProcess);
  192. }
  193. UNLOCK_PFN (OldIrql);
  194. return TRUE;
  195. }
  196. #if !defined(NT_UP) || defined (_IA64_)
  197. VOID
  198. MiSetDirtyBit (
  199. IN PVOID FaultingAddress,
  200. IN PMMPTE PointerPte,
  201. IN ULONG PfnHeld
  202. )
  203. /*++
  204. Routine Description:
  205. This routine sets dirty in the specified PTE and the modify bit in the
  206. corresponding PFN element. If any page file space is allocated, it
  207. is deallocated.
  208. Arguments:
  209. FaultingAddress - Supplies the faulting address.
  210. PointerPte - Supplies a pointer to the corresponding valid PTE.
  211. PfnHeld - Supplies TRUE if the PFN lock is already held.
  212. Return Value:
  213. None.
  214. Environment:
  215. Kernel mode, APCs disabled, Working set mutex held.
  216. --*/
  217. {
  218. MMPTE TempPte;
  219. PFN_NUMBER PageFrameIndex;
  220. PMMPFN Pfn1;
  221. //
  222. // The page is NOT copy on write, update the PTE setting both the
  223. // dirty bit and the accessed bit. Note, that as this PTE is in
  224. // the TB, the TB must be flushed.
  225. //
  226. TempPte = *PointerPte;
  227. MI_SET_PTE_DIRTY (TempPte);
  228. MI_SET_ACCESSED_IN_PTE (&TempPte, 1);
  229. MI_WRITE_VALID_PTE_NEW_PROTECTION(PointerPte, TempPte);
  230. //
  231. // Check state of PFN lock and if not held, don't update PFN database.
  232. //
  233. if (PfnHeld) {
  234. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE(PointerPte);
  235. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  236. //
  237. // Set the modified field in the PFN database, also, if the physical
  238. // page is currently in a paging file, free up the page file space
  239. // as the contents are now worthless.
  240. //
  241. if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
  242. (Pfn1->u3.e1.WriteInProgress == 0)) {
  243. //
  244. // This page is in page file format, deallocate the page file space.
  245. //
  246. MiReleasePageFileSpace (Pfn1->OriginalPte);
  247. //
  248. // Change original PTE to indicate no page file space is reserved,
  249. // otherwise the space will be deallocated when the PTE is
  250. // deleted.
  251. //
  252. Pfn1->OriginalPte.u.Soft.PageFileHigh = 0;
  253. }
  254. MI_SET_MODIFIED (Pfn1, 1, 0x17);
  255. }
  256. //
  257. // The TB entry must be flushed as the valid PTE with the dirty bit clear
  258. // has been fetched into the TB. If it isn't flushed, another fault
  259. // is generated as the dirty bit is not set in the cached TB entry.
  260. //
  261. KeFillEntryTb ((PHARDWARE_PTE)PointerPte, FaultingAddress, TRUE);
  262. return;
  263. }
  264. #endif