Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

wrtfault.c

Go to the documentation of this file.
00001 /*++ 00002 00003 Copyright (c) 1989 Microsoft Corporation 00004 00005 Module Name: 00006 00007 wrtfault.c 00008 00009 Abstract: 00010 00011 This module contains the copy on write routine for memory management. 00012 00013 Author: 00014 00015 Lou Perazzoli (loup) 10-Apr-1989 00016 00017 Revision History: 00018 00019 --*/ 00020 00021 #include "mi.h" 00022 00023 NTSTATUS 00024 FASTCALL 00025 MiCopyOnWrite ( 00026 IN PVOID FaultingAddress, 00027 IN PMMPTE PointerPte 00028 ) 00029 00030 /*++ 00031 00032 Routine Description: 00033 00034 This routine performs a copy on write operation for the specified 00035 virtual address. 00036 00037 Arguments: 00038 00039 FaultingAddress - Supplies the virtual address which caused the 00040 fault. 00041 00042 PointerPte - Supplies the pointer to the PTE which caused the 00043 page fault. 00044 00045 00046 Return Value: 00047 00048 Returns the status of the fault handling operation. Can be one of: 00049 - Success. 00050 - In-page Error. 00051 00052 Environment: 00053 00054 Kernel mode, APC's disabled, Working set mutex held. 00055 00056 --*/ 00057 00058 { 00059 MMPTE TempPte; 00060 PFN_NUMBER PageFrameIndex; 00061 PFN_NUMBER NewPageIndex; 00062 PULONG CopyTo; 00063 PULONG CopyFrom; 00064 KIRQL OldIrql; 00065 PMMPFN Pfn1; 00066 // PMMPTE PointerPde; 00067 PEPROCESS CurrentProcess; 00068 PMMCLONE_BLOCK CloneBlock; 00069 PMMCLONE_DESCRIPTOR CloneDescriptor; 00070 PVOID VirtualAddress; 00071 WSLE_NUMBER WorkingSetIndex; 00072 LOGICAL FakeCopyOnWrite; 00073 00074 FakeCopyOnWrite = FALSE; 00075 00076 // 00077 // This is called from MmAccessFault, the PointerPte is valid 00078 // and the working set mutex ensures it cannot change state. 00079 // 00080 00081 #if DBG 00082 if (MmDebug & MM_DBG_WRITEFAULT) { 00083 DbgPrint("**copy on write Fault va %lx proc %lx thread %lx\n", 00084 (ULONG_PTR)FaultingAddress, 00085 (ULONG_PTR)PsGetCurrentProcess(), (ULONG_PTR)PsGetCurrentThread()); 00086 } 00087 00088 if (MmDebug & MM_DBG_PTE_UPDATE) { 00089 MiFormatPte(PointerPte); 00090 } 00091 #endif //DBG 00092 00093 ASSERT (PsGetCurrentProcess()->ForkInProgress == NULL); 00094 00095 // 00096 // Capture the PTE contents to TempPte. 00097 // 00098 00099 TempPte = *PointerPte; 00100 00101 // 00102 // Check to see if this is a prototype PTE with copy on write 00103 // enabled. 00104 // 00105 00106 if (TempPte.u.Hard.CopyOnWrite == 0) { 00107 00108 // 00109 // This is a fork page which is being made private in order 00110 // to change the protection of the page. 00111 // Do not make the page writable. 00112 // 00113 00114 FakeCopyOnWrite = TRUE; 00115 } 00116 00117 PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte); 00118 Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); 00119 CurrentProcess = PsGetCurrentProcess(); 00120 00121 // 00122 // Acquire the PFN mutex. 00123 // 00124 00125 VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte); 00126 WorkingSetIndex = MiLocateWsle (VirtualAddress, MmWorkingSetList, 00127 Pfn1->u1.WsIndex); 00128 00129 LOCK_PFN (OldIrql); 00130 00131 // 00132 // The page must be copied into a new page. 00133 // 00134 00135 // 00136 // If a fork operation is in progress and the faulting thread 00137 // is not the thread performing the fork operation, block until 00138 // the fork is completed. 00139 // 00140 00141 if ((CurrentProcess->ForkInProgress != NULL) && 00142 (CurrentProcess->ForkInProgress != PsGetCurrentThread())) { 00143 MiWaitForForkToComplete (CurrentProcess); 00144 UNLOCK_PFN (OldIrql); 00145 return STATUS_SUCCESS; 00146 } 00147 00148 if (MiEnsureAvailablePageOrWait(CurrentProcess, NULL)) { 00149 00150 // 00151 // A wait operation was performed to obtain an available 00152 // page and the working set mutex and pfn mutexes have 00153 // been released and various things may have changed for 00154 // the worse. Rather than examine all the conditions again, 00155 // return and if things are still proper, the fault we 00156 // be taken again. 00157 // 00158 00159 UNLOCK_PFN (OldIrql); 00160 return STATUS_SUCCESS; 00161 } 00162 00163 // 00164 // Increment the number of private pages. 00165 // 00166 00167 CurrentProcess->NumberOfPrivatePages += 1; 00168 00169 MmInfoCounters.CopyOnWriteCount += 1; 00170 00171 // 00172 // A page is being copied and made private, the global state of 00173 // the shared page needs to be updated at this point on certain 00174 // hardware. This is done by ORing the dirty bit into the modify bit in 00175 // the PFN element. 00176 // 00177 00178 MI_CAPTURE_DIRTY_BIT_TO_PFN (PointerPte, Pfn1); 00179 00180 // 00181 // This must be a prototype PTE. Perform the copy on write. 00182 // 00183 00184 #if DBG 00185 if (Pfn1->u3.e1.PrototypePte == 0) { 00186 DbgPrint("writefault - PTE indicates cow but not protopte\n"); 00187 MiFormatPte(PointerPte); 00188 MiFormatPfn(Pfn1); 00189 } 00190 #endif 00191 00192 CloneBlock = (PMMCLONE_BLOCK)Pfn1->PteAddress; 00193 00194 // 00195 // If the share count for the physical page is one, the reference 00196 // count is one, and the modified flag is clear the current page 00197 // can be stolen to satisfy the copy on write. 00198 // 00199 00200 #if 0 00201 // COMMENTED OUT **************************************************** 00202 // COMMENTED OUT **************************************************** 00203 // COMMENTED OUT **************************************************** 00204 if ((Pfn1->u2.ShareCount == 1) && (Pfn1->u3.e2.ReferenceCount == 1) 00205 && (Pfn1->u3.e1.Modified == 0)) { 00206 00207 // 00208 // Make this page a private page and return the prototype 00209 // PTE into its original contents. The PFN database for 00210 // this page now points to this PTE. 00211 // 00212 00213 // 00214 // Note that a page fault could occur referencing the prototype 00215 // PTE, so we map it into hyperspace to prevent a fault. 00216 // 00217 00218 MiRestorePrototypePte (Pfn1); 00219 00220 Pfn1->PteAddress = PointerPte; 00221 00222 // 00223 // Get the protection for the page. 00224 // 00225 00226 VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte); 00227 WorkingSetIndex = MiLocateWsle (VirtualAddress, MmWorkingSetList, 00228 Pfn1->u1.WsIndex); 00229 00230 ASSERT (WorkingSetIndex != WSLE_NULL_INDEX) { 00231 00232 Pfn1->OriginalPte.u.Long = 0; 00233 Pfn1->OriginalPte.u.Soft.Protection = 00234 MI_MAKE_PROTECT_NOT_WRITE_COPY ( 00235 MmWsle[WorkingSetIndex].u1.e1.Protection); 00236 00237 PointerPde = MiGetPteAddress(PointerPte); 00238 Pfn1->u3.e1.PrototypePte = 0; 00239 Pfn1->PteFrame = MI_GET_PAGE_FRAME_FROM_PTE (PointerPde); 00240 00241 if (!FakeCopyOnWrite) { 00242 00243 // 00244 // If the page was Copy On Write and stolen or if the page was not 00245 // copy on write, update the PTE setting both the dirty bit and the 00246 // accessed bit. Note, that as this PTE is in the TB, the TB must 00247 // be flushed. 00248 // 00249 00250 MI_SET_PTE_DIRTY (TempPte); 00251 TempPte.u.Hard.Write = 1; 00252 MI_SET_ACCESSED_IN_PTE (&TempPte, 1); 00253 TempPte.u.Hard.CopyOnWrite = 0; 00254 MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, TempPte); 00255 00256 // 00257 // This is a copy on write operation, set the modify bit 00258 // in the PFN database and deallocate any page file space. 00259 // 00260 00261 Pfn1->u3.e1.Modified = 1; 00262 00263 if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) && 00264 (Pfn1->u3.e1.WriteInProgress == 0)) { 00265 00266 // 00267 // This page is in page file format, deallocate the page 00268 // file space. 00269 // 00270 00271 MiReleasePageFileSpace (Pfn1->OriginalPte); 00272 00273 // 00274 // Change original PTE to indicate no page file space is 00275 // reserved, otherwise the space will be deallocated when 00276 // the PTE is deleted. 00277 // 00278 00279 Pfn1->OriginalPte.u.Soft.PageFileHigh = 0; 00280 } 00281 } 00282 00283 // 00284 // The TB entry must be flushed as the valid PTE with the dirty 00285 // bit clear has been fetched into the TB. If it isn't flushed, 00286 // another fault is generated as the dirty bit is not set in 00287 // the cached TB entry. 00288 // 00289 00290 00291 KeFillEntryTb ((PHARDWARE_PTE)PointerPte, FaultingAddress, TRUE); 00292 00293 CloneDescriptor = MiLocateCloneAddress ((PVOID)CloneBlock); 00294 00295 if (CloneDescriptor != (PMMCLONE_DESCRIPTOR)NULL) { 00296 00297 // 00298 // Decrement the reference count for the clone block, 00299 // note that this could release and reacquire 00300 // the mutexes. 00301 // 00302 00303 MiDecrementCloneBlockReference ( CloneDescriptor, 00304 CloneBlock, 00305 CurrentProcess ); 00306 } 00307 00308 ] else [ 00309 00310 // ABOVE COMMENTED OUT **************************************************** 00311 // ABOVE COMMENTED OUT **************************************************** 00312 #endif 00313 00314 // 00315 // Get a new page with the same color as this page. 00316 // 00317 00318 NewPageIndex = MiRemoveAnyPage ( 00319 MI_GET_SECONDARY_COLOR (PageFrameIndex, 00320 Pfn1)); 00321 MiInitializeCopyOnWritePfn (NewPageIndex, PointerPte, WorkingSetIndex, NULL); 00322 00323 UNLOCK_PFN (OldIrql); 00324 00325 CopyTo = (PULONG)MiMapPageInHyperSpace (NewPageIndex, &OldIrql); 00326 00327 #if defined(_MIALT4K_) 00328 00329 // 00330 // Should avoid accessing the user space. Accessing the user space may potentially 00331 // cause a page fault on the alternate table. 00332 // 00333 00334 CopyFrom = KSEG_ADDRESS(PointerPte->u.Hard.PageFrameNumber); 00335 00336 #else 00337 CopyFrom = (PULONG)MiGetVirtualAddressMappedByPte (PointerPte); 00338 #endif 00339 00340 RtlCopyMemory ( CopyTo, CopyFrom, PAGE_SIZE); 00341 00342 PERFINFO_PRIVATE_COPY_ON_WRITE(CopyFrom, PAGE_SIZE); 00343 00344 MiUnmapPageInHyperSpace (OldIrql); 00345 00346 if (!FakeCopyOnWrite) { 00347 00348 // 00349 // If the page was really a copy on write page, make it 00350 // accessed, dirty and writable. Also, clear the copy-on-write 00351 // bit in the PTE. 00352 // 00353 00354 MI_SET_PTE_DIRTY (TempPte); 00355 TempPte.u.Hard.Write = 1; 00356 MI_SET_ACCESSED_IN_PTE (&TempPte, 1); 00357 TempPte.u.Hard.CopyOnWrite = 0; 00358 TempPte.u.Hard.PageFrameNumber = NewPageIndex; 00359 00360 } else { 00361 00362 // 00363 // The page was not really a copy on write, just change 00364 // the frame field of the PTE. 00365 // 00366 00367 TempPte.u.Hard.PageFrameNumber = NewPageIndex; 00368 } 00369 00370 // 00371 // If the modify bit is set in the PFN database for the 00372 // page, the data cache must be flushed. This is due to the 00373 // fact that this process may have been cloned and the cache 00374 // still contains stale data destined for the page we are 00375 // going to remove. 00376 // 00377 00378 ASSERT (TempPte.u.Hard.Valid == 1); 00379 00380 LOCK_PFN (OldIrql); 00381 00382 // 00383 // Flush the TB entry for this page. 00384 // 00385 00386 KeFlushSingleTb (FaultingAddress, 00387 TRUE, 00388 FALSE, 00389 (PHARDWARE_PTE)PointerPte, 00390 TempPte.u.Flush); 00391 00392 // 00393 // Decrement the share count for the page which was copied 00394 // as this pte no longer refers to it. 00395 // 00396 00397 MiDecrementShareCount (PageFrameIndex); 00398 00399 CloneDescriptor = MiLocateCloneAddress ((PVOID)CloneBlock); 00400 00401 if (CloneDescriptor != (PMMCLONE_DESCRIPTOR)NULL) { 00402 00403 // 00404 // Decrement the reference count for the clone block, 00405 // note that this could release and reacquire 00406 // the mutexes. 00407 // 00408 00409 MiDecrementCloneBlockReference ( CloneDescriptor, 00410 CloneBlock, 00411 CurrentProcess ); 00412 } 00413 00414 UNLOCK_PFN (OldIrql); 00415 return STATUS_SUCCESS; 00416 }

Generated on Sat May 15 19:42:28 2004 for test by doxygen 1.3.7