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

flushsec.c

Go to the documentation of this file.
00001 /*++ 00002 00003 Copyright (c) 1990 Microsoft Corporation 00004 00005 Module Name: 00006 00007 flushsec.c 00008 00009 Abstract: 00010 00011 This module contains the routines which implement the 00012 NtFlushVirtualMemory service. 00013 00014 Author: 00015 00016 Lou Perazzoli (loup) 8-May-1990 00017 Landy Wang (landyw) 02-June-1997 00018 00019 Revision History: 00020 00021 --*/ 00022 00023 #include "mi.h" 00024 00025 PSUBSECTION 00026 MiGetSystemCacheSubsection ( 00027 IN PVOID BaseAddress, 00028 IN PEPROCESS Process, 00029 OUT PMMPTE *ProtoPte 00030 ); 00031 00032 VOID 00033 MiFlushDirtyBitsToPfn ( 00034 IN PMMPTE PointerPte, 00035 IN PMMPTE LastPte, 00036 IN PEPROCESS Process, 00037 IN BOOLEAN SystemCache 00038 ); 00039 00040 ULONG 00041 FASTCALL 00042 MiCheckProtoPtePageState ( 00043 IN PMMPTE PrototypePte, 00044 IN ULONG PfnLockHeld 00045 ); 00046 00047 #ifdef ALLOC_PRAGMA 00048 #pragma alloc_text(PAGE,NtFlushVirtualMemory) 00049 #pragma alloc_text(PAGE,MmFlushVirtualMemory) 00050 #endif 00051 00052 extern POBJECT_TYPE IoFileObjectType; 00053 00054 NTSTATUS 00055 NtFlushVirtualMemory ( 00056 IN HANDLE ProcessHandle, 00057 IN OUT PVOID *BaseAddress, 00058 IN OUT PSIZE_T RegionSize, 00059 OUT PIO_STATUS_BLOCK IoStatus 00060 ) 00061 00062 /*++ 00063 00064 Routine Description: 00065 00066 This function flushes a range of virtual address which map 00067 a data file back into the data file if they have been modified. 00068 00069 Arguments: 00070 00071 ProcessHandle - Supplies an open handle to a process object. 00072 00073 BaseAddress - Supplies a pointer to a variable that will receive 00074 the base address the flushed region. The initial value 00075 of this argument is the base address of the region of the 00076 pages to flush. 00077 00078 RegionSize - Supplies a pointer to a variable that will receive 00079 the actual size in bytes of the flushed region of pages. 00080 The initial value of this argument is rounded up to the 00081 next host-page-size boundary. 00082 00083 If this value is specified as zero, the mapped range from 00084 the base address to the end of the range is flushed. 00085 00086 IoStatus - Returns the value of the IoStatus for the last attempted 00087 I/O operation. 00088 00089 Return Value: 00090 00091 Returns the status 00092 00093 TBS 00094 00095 00096 --*/ 00097 00098 { 00099 PEPROCESS Process; 00100 KPROCESSOR_MODE PreviousMode; 00101 NTSTATUS Status; 00102 PVOID CapturedBase; 00103 SIZE_T CapturedRegionSize; 00104 IO_STATUS_BLOCK TemporaryIoStatus; 00105 00106 PAGED_CODE(); 00107 00108 PreviousMode = KeGetPreviousMode(); 00109 if (PreviousMode != KernelMode) { 00110 00111 // 00112 // Establish an exception handler, probe the specified addresses 00113 // for write access and capture the initial values. 00114 // 00115 00116 try { 00117 00118 ProbeForWritePointer (BaseAddress); 00119 ProbeForWriteUlong_ptr (RegionSize); 00120 ProbeForWriteIoStatus (IoStatus); 00121 00122 // 00123 // Capture the base address. 00124 // 00125 00126 CapturedBase = *BaseAddress; 00127 00128 // 00129 // Capture the region size. 00130 // 00131 00132 CapturedRegionSize = *RegionSize; 00133 00134 } except (EXCEPTION_EXECUTE_HANDLER) { 00135 00136 // 00137 // If an exception occurs during the probe or capture 00138 // of the initial values, then handle the exception and 00139 // return the exception code as the status value. 00140 // 00141 00142 return GetExceptionCode(); 00143 } 00144 00145 } else { 00146 00147 // 00148 // Capture the base address. 00149 // 00150 00151 CapturedBase = *BaseAddress; 00152 00153 // 00154 // Capture the region size. 00155 // 00156 00157 CapturedRegionSize = *RegionSize; 00158 00159 } 00160 00161 // 00162 // Make sure the specified starting and ending addresses are 00163 // within the user part of the virtual address space. 00164 // 00165 00166 if (CapturedBase > MM_HIGHEST_USER_ADDRESS) { 00167 00168 // 00169 // Invalid base address. 00170 // 00171 00172 return STATUS_INVALID_PARAMETER_2; 00173 } 00174 00175 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)CapturedBase) < 00176 CapturedRegionSize) { 00177 00178 // 00179 // Invalid region size; 00180 // 00181 00182 return STATUS_INVALID_PARAMETER_2; 00183 00184 } 00185 00186 Status = ObReferenceObjectByHandle ( ProcessHandle, 00187 PROCESS_VM_OPERATION, 00188 PsProcessType, 00189 PreviousMode, 00190 (PVOID *)&Process, 00191 NULL ); 00192 if (!NT_SUCCESS(Status)) { 00193 return Status; 00194 } 00195 00196 Status = MmFlushVirtualMemory (Process, 00197 &CapturedBase, 00198 &CapturedRegionSize, 00199 &TemporaryIoStatus); 00200 00201 ObDereferenceObject (Process); 00202 00203 // 00204 // Establish an exception handler and write the size and base 00205 // address. 00206 // 00207 00208 try { 00209 00210 *RegionSize = CapturedRegionSize; 00211 *BaseAddress = PAGE_ALIGN (CapturedBase); 00212 *IoStatus = TemporaryIoStatus; 00213 00214 } except (EXCEPTION_EXECUTE_HANDLER) { 00215 } 00216 00217 return Status; 00218 00219 } 00220 00221 00222 VOID 00223 MiFlushAcquire ( 00224 IN PCONTROL_AREA ControlArea 00225 ) 00226 00227 /*++ 00228 00229 Routine Description: 00230 00231 This is a helper routine to reference count the control area if needed 00232 during a flush section call to prevent the section object from being 00233 deleted while the flush is ongoing. 00234 00235 Arguments: 00236 00237 ControlArea - Supplies a pointer to the control area. 00238 00239 Return Value: 00240 00241 None. 00242 00243 --*/ 00244 00245 { 00246 KIRQL OldIrql; 00247 00248 LOCK_PFN (OldIrql); 00249 00250 ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1); 00251 ControlArea->NumberOfMappedViews += 1; 00252 00253 UNLOCK_PFN (OldIrql); 00254 } 00255 00256 00257 VOID 00258 MiFlushRelease ( 00259 IN PCONTROL_AREA ControlArea 00260 ) 00261 00262 /*++ 00263 00264 Routine Description: 00265 00266 This is a helper routine to release the control area reference needed 00267 during a flush section call. 00268 00269 Arguments: 00270 00271 ControlArea - Supplies a pointer to the control area. 00272 00273 Return Value: 00274 00275 None. 00276 00277 --*/ 00278 00279 { 00280 KIRQL OldIrql; 00281 00282 LOCK_PFN (OldIrql); 00283 00284 ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1); 00285 ControlArea->NumberOfMappedViews -= 1; 00286 00287 // 00288 // Check to see if the control area should be deleted. This 00289 // will release the PFN lock. 00290 // 00291 00292 MiCheckControlArea (ControlArea, NULL, OldIrql); 00293 } 00294 00295 00296 NTSTATUS 00297 MmFlushVirtualMemory ( 00298 IN PEPROCESS Process, 00299 IN OUT PVOID *BaseAddress, 00300 IN OUT PSIZE_T RegionSize, 00301 OUT PIO_STATUS_BLOCK IoStatus 00302 ) 00303 00304 /*++ 00305 00306 Routine Description: 00307 00308 This function flushes a range of virtual address which map 00309 a data file back into the data file if they have been modified. 00310 00311 Note that the modification is this process's view of the pages, 00312 on certain implementations (like the Intel 386), the modify 00313 bit is captured in the PTE and not forced to the PFN database 00314 until the page is removed from the working set. This means 00315 that pages which have been modified by another process will 00316 not be flushed to the data file. 00317 00318 Arguments: 00319 00320 Process - Supplies a pointer to a process object. 00321 00322 BaseAddress - Supplies a pointer to a variable that will receive 00323 the base address of the flushed region. The initial value 00324 of this argument is the base address of the region of the 00325 pages to flush. 00326 00327 RegionSize - Supplies a pointer to a variable that will receive 00328 the actual size in bytes of the flushed region of pages. 00329 The initial value of this argument is rounded up to the 00330 next host-page-size boundary. 00331 00332 If this value is specified as zero, the mapped range from 00333 the base address to the end of the range is flushed. 00334 00335 IoStatus - Returns the value of the IoStatus for the last attempted 00336 I/O operation. 00337 00338 Return Value: 00339 00340 Returns the NT status 00341 00342 --*/ 00343 00344 { 00345 PMMVAD Vad; 00346 PVOID EndingAddress; 00347 PVOID Va; 00348 PEPROCESS CurrentProcess; 00349 BOOLEAN SystemCache; 00350 PCONTROL_AREA ControlArea; 00351 PMMPTE PointerPte; 00352 PMMPTE PointerPde; 00353 PMMPTE PointerPpe; 00354 PMMPTE LastPte; 00355 PMMPTE FinalPte; 00356 PSUBSECTION Subsection; 00357 PSUBSECTION LastSubsection; 00358 NTSTATUS Status; 00359 ULONG ConsecutiveFileLockFailures; 00360 ULONG Waited; 00361 LOGICAL EntireRestOfVad; 00362 LOGICAL Attached; 00363 00364 PAGED_CODE(); 00365 00366 Attached = FALSE; 00367 00368 // 00369 // Determine if the specified base address is within the system 00370 // cache and if so, don't attach, the working set mutex is still 00371 // required to "lock" paged pool pages (proto PTEs) into the 00372 // working set. 00373 // 00374 00375 EndingAddress = (PVOID)(((ULONG_PTR)*BaseAddress + *RegionSize - 1) | 00376 (PAGE_SIZE - 1)); 00377 *BaseAddress = PAGE_ALIGN (*BaseAddress); 00378 00379 if (MI_IS_SESSION_ADDRESS (*BaseAddress)) { 00380 00381 // 00382 // Nothing in session space needs flushing. 00383 // 00384 00385 return STATUS_NOT_MAPPED_VIEW; 00386 } 00387 00388 CurrentProcess = PsGetCurrentProcess (); 00389 00390 if (!MI_IS_SYSTEM_CACHE_ADDRESS(*BaseAddress)) { 00391 00392 SystemCache = FALSE; 00393 00394 // 00395 // Attach to the specified process. 00396 // 00397 00398 if (PsGetCurrentProcess() != Process) { 00399 KeAttachProcess (&Process->Pcb); 00400 Attached = TRUE; 00401 } 00402 00403 LOCK_WS_AND_ADDRESS_SPACE (Process); 00404 00405 // 00406 // Make sure the address space was not deleted, if so, return an error. 00407 // 00408 00409 if (Process->AddressSpaceDeleted != 0) { 00410 Status = STATUS_PROCESS_IS_TERMINATING; 00411 goto ErrorReturn; 00412 } 00413 00414 Vad = MiLocateAddress (*BaseAddress); 00415 00416 if (Vad == (PMMVAD)NULL) { 00417 00418 // 00419 // No Virtual Address Descriptor located for Base Address. 00420 // 00421 00422 Status = STATUS_NOT_MAPPED_VIEW; 00423 goto ErrorReturn; 00424 } 00425 00426 if (*RegionSize == 0) { 00427 EndingAddress = MI_VPN_TO_VA_ENDING (Vad->EndingVpn); 00428 EntireRestOfVad = TRUE; 00429 } 00430 else { 00431 EntireRestOfVad = FALSE; 00432 } 00433 00434 if ((Vad->u.VadFlags.PrivateMemory == 1) || 00435 (MI_VA_TO_VPN (EndingAddress) > Vad->EndingVpn)) { 00436 00437 // 00438 // This virtual address descriptor does not refer to a Segment 00439 // object. 00440 // 00441 00442 Status = STATUS_NOT_MAPPED_VIEW; 00443 goto ErrorReturn; 00444 } 00445 00446 // 00447 // Make sure this VAD maps a data file (not an image file). 00448 // 00449 00450 ControlArea = Vad->ControlArea; 00451 00452 if ((ControlArea->FilePointer == NULL) || 00453 (Vad->u.VadFlags.ImageMap == 1)) { 00454 00455 // 00456 // This virtual address descriptor does not refer to a Segment 00457 // object. 00458 // 00459 00460 Status = STATUS_NOT_MAPPED_DATA; 00461 goto ErrorReturn; 00462 } 00463 00464 } else { 00465 00466 SystemCache = TRUE; 00467 Process = CurrentProcess; 00468 LOCK_WS (Process); 00469 } 00470 00471 PointerPpe = MiGetPpeAddress (*BaseAddress); 00472 PointerPde = MiGetPdeAddress (*BaseAddress); 00473 PointerPte = MiGetPteAddress (*BaseAddress); 00474 LastPte = MiGetPteAddress (EndingAddress); 00475 *RegionSize = (PCHAR)EndingAddress - (PCHAR)*BaseAddress + 1; 00476 00477 retry: 00478 00479 while (!MiDoesPpeExistAndMakeValid (PointerPpe, Process, FALSE, &Waited)) { 00480 00481 // 00482 // This page directory parent entry is empty, go to the next one. 00483 // 00484 00485 PointerPpe += 1; 00486 PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe); 00487 PointerPte = MiGetVirtualAddressMappedByPte (PointerPde); 00488 Va = MiGetVirtualAddressMappedByPte (PointerPte); 00489 00490 if (PointerPte > LastPte) { 00491 break; 00492 } 00493 } 00494 00495 Waited = 0; 00496 00497 if (PointerPte <= LastPte) { 00498 while (!MiDoesPdeExistAndMakeValid(PointerPde, Process, FALSE, &Waited)) { 00499 00500 // 00501 // No page table page exists for this address. 00502 // 00503 00504 PointerPde += 1; 00505 00506 PointerPte = MiGetVirtualAddressMappedByPte (PointerPde); 00507 00508 if (PointerPte > LastPte) { 00509 break; 00510 } 00511 00512 #if defined (_WIN64) 00513 if (MiIsPteOnPdeBoundary (PointerPde)) { 00514 PointerPpe = MiGetPteAddress (PointerPde); 00515 goto retry; 00516 } 00517 #endif 00518 00519 Va = MiGetVirtualAddressMappedByPte (PointerPte); 00520 } 00521 00522 // 00523 // If the PFN lock (and accordingly the WS mutex) was 00524 // released and reacquired we must retry the operation. 00525 // 00526 00527 if (PointerPte <= LastPte && Waited != 0) { 00528 goto retry; 00529 } 00530 } 00531 00532 MiFlushDirtyBitsToPfn (PointerPte, LastPte, Process, SystemCache); 00533 00534 if (SystemCache) { 00535 00536 // 00537 // No VADs exist for the system cache. 00538 // 00539 00540 Subsection = MiGetSystemCacheSubsection (*BaseAddress, 00541 Process, 00542 &PointerPte); 00543 LastSubsection = MiGetSystemCacheSubsection (EndingAddress, 00544 Process, 00545 &FinalPte); 00546 UNLOCK_WS (Process); 00547 00548 // 00549 // Flush the PTEs from the specified section. 00550 // 00551 00552 Status = MiFlushSectionInternal (PointerPte, 00553 FinalPte, 00554 Subsection, 00555 LastSubsection, 00556 FALSE, 00557 TRUE, 00558 IoStatus); 00559 } 00560 else { 00561 00562 // 00563 // Protect against the section being prematurely deleted. 00564 // 00565 00566 MiFlushAcquire (ControlArea); 00567 00568 PointerPte = MiGetProtoPteAddress (Vad, MI_VA_TO_VPN (*BaseAddress)); 00569 Subsection = MiLocateSubsection (Vad, MI_VA_TO_VPN(*BaseAddress)); 00570 LastSubsection = MiLocateSubsection (Vad, MI_VA_TO_VPN(EndingAddress)); 00571 00572 // 00573 // The last subsection is NULL if the section is not fully 00574 // committed. Only allow the flush if the caller said do the whole 00575 // thing, otherwise it's an error. 00576 // 00577 00578 if (LastSubsection == NULL) { 00579 00580 if (EntireRestOfVad == FALSE) { 00581 00582 // 00583 // Caller can only specify the range that is committed or zero 00584 // to indicate the entire range. 00585 // 00586 00587 UNLOCK_WS_AND_ADDRESS_SPACE (Process); 00588 if (Attached == TRUE) { 00589 KeDetachProcess(); 00590 } 00591 MiFlushRelease (ControlArea); 00592 return STATUS_NOT_MAPPED_VIEW; 00593 } 00594 00595 LastSubsection = Subsection; 00596 while (LastSubsection->NextSubsection) { 00597 LastSubsection = LastSubsection->NextSubsection; 00598 } 00599 FinalPte = LastSubsection->SubsectionBase + LastSubsection->PtesInSubsection - 1; 00600 } 00601 else { 00602 FinalPte = MiGetProtoPteAddress (Vad, MI_VA_TO_VPN (EndingAddress)); 00603 } 00604 00605 UNLOCK_WS_AND_ADDRESS_SPACE (Process); 00606 if (Attached == TRUE) { 00607 KeDetachProcess(); 00608 } 00609 00610 // 00611 // Preacquire the file to synchronize the flush. 00612 // 00613 00614 ConsecutiveFileLockFailures = 0; 00615 00616 do { 00617 00618 FsRtlAcquireFileForCcFlush (ControlArea->FilePointer); 00619 00620 // 00621 // Flush the PTEs from the specified section. 00622 // 00623 00624 Status = MiFlushSectionInternal (PointerPte, 00625 FinalPte, 00626 Subsection, 00627 LastSubsection, 00628 TRUE, 00629 TRUE, 00630 IoStatus); 00631 00632 // 00633 // Release the file we acquired. 00634 // 00635 00636 FsRtlReleaseFileForCcFlush (ControlArea->FilePointer); 00637 00638 // 00639 // Only try the request more than once if the filesystem told us 00640 // it had a deadlock. 00641 // 00642 00643 if (Status != STATUS_FILE_LOCK_CONFLICT) { 00644 break; 00645 } 00646 00647 ConsecutiveFileLockFailures += 1; 00648 KeDelayExecutionThread (KernelMode, FALSE, &MmShortTime); 00649 00650 } while (ConsecutiveFileLockFailures < 5); 00651 00652 MiFlushRelease (ControlArea); 00653 } 00654 00655 return Status; 00656 00657 ErrorReturn: 00658 ASSERT (SystemCache == FALSE); 00659 UNLOCK_WS_AND_ADDRESS_SPACE (Process); 00660 if (Attached == TRUE) { 00661 KeDetachProcess(); 00662 } 00663 return Status; 00664 00665 } 00666 00667 NTSTATUS 00668 MmFlushSection ( 00669 IN PSECTION_OBJECT_POINTERS SectionObjectPointer, 00670 IN PLARGE_INTEGER Offset, 00671 IN SIZE_T RegionSize, 00672 OUT PIO_STATUS_BLOCK IoStatus, 00673 IN ULONG AcquireFile 00674 ) 00675 00676 /*++ 00677 00678 Routine Description: 00679 00680 This function flushes to the backing file any modified pages within 00681 the specified range of the section. 00682 00683 Arguments: 00684 00685 SectionObjectPointer - Supplies a pointer to the section objects. 00686 00687 Offset - Supplies the offset into the section in which to begin 00688 flushing pages. If this argument is not present, then the 00689 whole section is flushed without regard to the region size 00690 argument. 00691 00692 RegionSize - Supplies the size in bytes to flush. This is rounded 00693 to a page multiple. 00694 00695 IoStatus - Returns the value of the IoStatus for the last attempted 00696 I/O operation. 00697 00698 AcquireFile - Nonzero if the callback should be used to acquire the file 00699 00700 Return Value: 00701 00702 Returns status of the operation. 00703 00704 --*/ 00705 00706 { 00707 PCONTROL_AREA ControlArea; 00708 PMMPTE PointerPte; 00709 PMMPTE LastPte; 00710 KIRQL OldIrql; 00711 ULONG PteOffset; 00712 PSUBSECTION Subsection; 00713 PSUBSECTION LastSubsection; 00714 BOOLEAN DeleteSegment = FALSE; 00715 PETHREAD CurrentThread; 00716 NTSTATUS status; 00717 BOOLEAN OldClusterState; 00718 ULONG ConsecutiveFileLockFailures; 00719 00720 // 00721 // Initialize IoStatus for success, in case we take an early exit. 00722 // 00723 00724 IoStatus->Status = STATUS_SUCCESS; 00725 IoStatus->Information = RegionSize; 00726 00727 LOCK_PFN (OldIrql); 00728 00729 ControlArea = ((PCONTROL_AREA)(SectionObjectPointer->DataSectionObject)); 00730 00731 ASSERT ((ControlArea == NULL) || (ControlArea->u.Flags.Image == 0)); 00732 00733 if ((ControlArea == NULL) || 00734 (ControlArea->u.Flags.BeingDeleted) || 00735 (ControlArea->u.Flags.BeingCreated) || 00736 (ControlArea->NumberOfPfnReferences == 0)) { 00737 00738 // 00739 // This file no longer has an associated segment or is in the 00740 // process of coming or going. 00741 // If the number of PFN references is zero, then this control 00742 // area does not have any valid or transition pages that need 00743 // to be flushed. 00744 // 00745 00746 UNLOCK_PFN (OldIrql); 00747 return STATUS_SUCCESS; 00748 } 00749 00750 // 00751 // Locate the subsection. 00752 // 00753 00754 ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0); 00755 00756 Subsection = (PSUBSECTION)(ControlArea + 1); 00757 00758 if (!ARGUMENT_PRESENT (Offset)) { 00759 00760 // 00761 // If the offset is not specified, flush the complete file ignoring 00762 // the region size. 00763 // 00764 00765 PointerPte = &Subsection->SubsectionBase[0]; 00766 LastSubsection = Subsection; 00767 while (LastSubsection->NextSubsection != NULL) { 00768 LastSubsection = LastSubsection->NextSubsection; 00769 } 00770 LastPte = &LastSubsection->SubsectionBase 00771 [LastSubsection->PtesInSubsection - 1]; 00772 } else { 00773 00774 PteOffset = (ULONG)(Offset->QuadPart >> PAGE_SHIFT); 00775 00776 // 00777 // Make sure the PTEs are not in the extended part of the 00778 // segment. 00779 // 00780 00781 while (PteOffset >= Subsection->PtesInSubsection) { 00782 PteOffset -= Subsection->PtesInSubsection; 00783 if (Subsection->NextSubsection == NULL) { 00784 00785 // 00786 // Past end of mapping, just return success. 00787 // 00788 00789 UNLOCK_PFN (OldIrql); 00790 return STATUS_SUCCESS; 00791 } 00792 Subsection = Subsection->NextSubsection; 00793 } 00794 00795 ASSERT (PteOffset < Subsection->PtesInSubsection); 00796 PointerPte = &Subsection->SubsectionBase[PteOffset]; 00797 00798 // 00799 // Locate the address of the last prototype PTE to be flushed. 00800 // 00801 00802 PteOffset += (ULONG)(((RegionSize + BYTE_OFFSET(Offset->LowPart)) - 1) >> PAGE_SHIFT); 00803 00804 LastSubsection = Subsection; 00805 00806 while (PteOffset >= LastSubsection->PtesInSubsection) { 00807 PteOffset -= LastSubsection->PtesInSubsection; 00808 if (LastSubsection->NextSubsection == NULL) { 00809 PteOffset = LastSubsection->PtesInSubsection - 1; 00810 break; 00811 } 00812 LastSubsection = LastSubsection->NextSubsection; 00813 } 00814 00815 ASSERT (PteOffset < LastSubsection->PtesInSubsection); 00816 LastPte = &LastSubsection->SubsectionBase[PteOffset]; 00817 } 00818 00819 // 00820 // Up the map view count so the control area cannot be deleted 00821 // out from under the call. 00822 // 00823 00824 ControlArea->NumberOfMappedViews += 1; 00825 00826 UNLOCK_PFN (OldIrql); 00827 00828 CurrentThread = PsGetCurrentThread(); 00829 00830 // 00831 // Indicate that disk verify errors should be returned as exceptions. 00832 // 00833 00834 OldClusterState = CurrentThread->ForwardClusterOnly; 00835 CurrentThread->ForwardClusterOnly = TRUE; 00836 00837 // 00838 // Preacquire the file if we are going to synchronize the flush. 00839 // 00840 00841 if (AcquireFile == 0) { 00842 00843 // 00844 // Flush the PTEs from the specified section. 00845 // 00846 00847 status = MiFlushSectionInternal (PointerPte, 00848 LastPte, 00849 Subsection, 00850 LastSubsection, 00851 TRUE, 00852 TRUE, 00853 IoStatus); 00854 } 00855 else { 00856 00857 ConsecutiveFileLockFailures = 0; 00858 00859 do { 00860 00861 FsRtlAcquireFileForCcFlush (ControlArea->FilePointer); 00862 00863 // 00864 // Flush the PTEs from the specified section. 00865 // 00866 00867 status = MiFlushSectionInternal (PointerPte, 00868 LastPte, 00869 Subsection, 00870 LastSubsection, 00871 TRUE, 00872 TRUE, 00873 IoStatus); 00874 00875 // 00876 // Release the file we acquired. 00877 // 00878 00879 FsRtlReleaseFileForCcFlush (ControlArea->FilePointer); 00880 00881 // 00882 // Only try the request more than once if the filesystem told us 00883 // it had a deadlock. 00884 // 00885 00886 if (status != STATUS_FILE_LOCK_CONFLICT) { 00887 break; 00888 } 00889 00890 ConsecutiveFileLockFailures += 1; 00891 KeDelayExecutionThread (KernelMode, FALSE, &MmShortTime); 00892 00893 } while (ConsecutiveFileLockFailures < 5); 00894 } 00895 00896 CurrentThread->ForwardClusterOnly = OldClusterState; 00897 00898 LOCK_PFN (OldIrql); 00899 00900 ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1); 00901 ControlArea->NumberOfMappedViews -= 1; 00902 00903 // 00904 // Check to see if the control area should be deleted. This 00905 // will release the PFN lock. 00906 // 00907 00908 MiCheckControlArea (ControlArea, NULL, OldIrql); 00909 00910 return status; 00911 00912 } 00913 00914 00915 LONGLONG 00916 MiStartingOffset( 00917 IN PSUBSECTION Subsection, 00918 IN PMMPTE PteAddress 00919 ) 00920 00921 /*++ 00922 00923 Routine Description: 00924 00925 This function calculates the file offset given a subsection and a PTE 00926 offset. Note that images are stored in 512-byte units whereas data is 00927 stored in 4K units. 00928 00929 When this is all debugged, this should be made into a macro. 00930 00931 Arguments: 00932 00933 Subsection - Supplies a subsection to reference for the file address. 00934 00935 PteAddress - Supplies a PTE within the subsection 00936 00937 Return Value: 00938 00939 Returns the file offset to obtain the backing data from. 00940 00941 --*/ 00942 00943 { 00944 LONGLONG PteByteOffset; 00945 LARGE_INTEGER StartAddress; 00946 00947 if (Subsection->ControlArea->u.Flags.Image == 1) { 00948 return MI_STARTING_OFFSET ( Subsection, 00949 PteAddress); 00950 } 00951 00952 PteByteOffset = (LONGLONG)((PteAddress - Subsection->SubsectionBase)) 00953 << PAGE_SHIFT; 00954 00955 Mi4KStartFromSubsection (&StartAddress, Subsection); 00956 00957 StartAddress.QuadPart = StartAddress.QuadPart << MM4K_SHIFT; 00958 00959 PteByteOffset += StartAddress.QuadPart; 00960 00961 return PteByteOffset; 00962 } 00963 00964 LARGE_INTEGER 00965 MiEndingOffset( 00966 IN PSUBSECTION Subsection 00967 ) 00968 00969 /*++ 00970 00971 Routine Description: 00972 00973 This function calculates the last valid file offset in a given subsection. 00974 offset. Note that images are stored in 512-byte units whereas data is 00975 stored in 4K units. 00976 00977 When this is all debugged, this should be made into a macro. 00978 00979 Arguments: 00980 00981 Subsection - Supplies a subsection to reference for the file address. 00982 00983 PteAddress - Supplies a PTE within the subsection 00984 00985 Return Value: 00986 00987 Returns the file offset to obtain the backing data from. 00988 00989 --*/ 00990 00991 { 00992 LARGE_INTEGER FileByteOffset; 00993 00994 if (Subsection->ControlArea->u.Flags.Image == 1) { 00995 FileByteOffset.QuadPart = 00996 (Subsection->StartingSector + Subsection->NumberOfFullSectors) << 00997 MMSECTOR_SHIFT; 00998 } 00999 else { 01000 Mi4KStartFromSubsection (&FileByteOffset, Subsection); 01001 01002 FileByteOffset.QuadPart += Subsection->NumberOfFullSectors; 01003 01004 FileByteOffset.QuadPart = FileByteOffset.QuadPart << MM4K_SHIFT; 01005 } 01006 01007 FileByteOffset.QuadPart += Subsection->u.SubsectionFlags.SectorEndOffset; 01008 01009 return FileByteOffset; 01010 } 01011 01012 01013 NTSTATUS 01014 MiFlushSectionInternal ( 01015 IN PMMPTE StartingPte, 01016 IN PMMPTE FinalPte, 01017 IN PSUBSECTION FirstSubsection, 01018 IN PSUBSECTION LastSubsection, 01019 IN ULONG Synchronize, 01020 IN LOGICAL WriteInProgressOk, 01021 OUT PIO_STATUS_BLOCK IoStatus 01022 ) 01023 01024 /*++ 01025 01026 Routine Description: 01027 01028 This function flushes to the backing file any modified pages within 01029 the specified range of the section. The parameters describe the 01030 section's prototype PTEs (start and end) and the subsections 01031 which correspond to the starting and ending PTE. 01032 01033 Each PTE in the subsection between the specified start and end 01034 is examined and if the page is either valid or transition AND 01035 the page has been modified, the modify bit is cleared in the PFN 01036 database and the page is flushed to its backing file. 01037 01038 Arguments: 01039 01040 StartingPte - Supplies a pointer to the first prototype PTE to 01041 be examined for flushing. 01042 01043 FinalPte - Supplies a pointer to the last prototype PTE to be 01044 examined for flushing. 01045 01046 FirstSubsection - Supplies the subsection that contains the 01047 StartingPte. 01048 01049 LastSubsection - Supplies the subsection that contains the 01050 FinalPte. 01051 01052 Synchronize - Supplies TRUE if synchronization with all threads 01053 doing flush operations to this section should occur. 01054 01055 WriteInProgressOk - Supplies TRUE if the caller can tolerate a write 01056 already in progress for any dirty pages. 01057 01058 IoStatus - Returns the value of the IoStatus for the last attempted 01059 I/O operation. 01060 01061 Return Value: 01062 01063 Returns status of the operation. 01064 01065 --*/ 01066 01067 { 01068 PCONTROL_AREA ControlArea; 01069 PMMPTE PointerPte; 01070 PMMPTE LastPte; 01071 PMMPTE LastWritten; 01072 MMPTE PteContents; 01073 PMMPFN Pfn1; 01074 PMMPFN Pfn2; 01075 KIRQL OldIrql; 01076 PMDL Mdl; 01077 KEVENT IoEvent; 01078 PSUBSECTION Subsection; 01079 PPFN_NUMBER Page; 01080 PFN_NUMBER PageFrameIndex; 01081 PPFN_NUMBER LastPage; 01082 NTSTATUS Status; 01083 UINT64 StartingOffset; 01084 UINT64 TempOffset; 01085 BOOLEAN WriteNow; 01086 LOGICAL Bail; 01087 PFN_NUMBER MdlHack[(sizeof(MDL)/sizeof(PFN_NUMBER)) + (MM_MAXIMUM_DISK_IO_SIZE / PAGE_SIZE) + 1]; 01088 ULONG ReflushCount; 01089 01090 WriteNow = FALSE; 01091 Bail = FALSE; 01092 01093 IoStatus->Status = STATUS_SUCCESS; 01094 IoStatus->Information = 0; 01095 Mdl = (PMDL)&MdlHack[0]; 01096 01097 KeInitializeEvent (&IoEvent, NotificationEvent, FALSE); 01098 01099 FinalPte += 1; // Point to 1 past the last one. 01100 01101 LastWritten = NULL; 01102 LastPage = 0; 01103 Subsection = FirstSubsection; 01104 PointerPte = StartingPte; 01105 ControlArea = FirstSubsection->ControlArea; 01106 01107 LOCK_PFN (OldIrql); 01108 01109 ASSERT (ControlArea->u.Flags.Image == 0); 01110 01111 if (ControlArea->NumberOfPfnReferences == 0) { 01112 01113 // 01114 // No transition or valid prototype PTEs present, hence 01115 // no need to flush anything. 01116 // 01117 01118 UNLOCK_PFN (OldIrql); 01119 return STATUS_SUCCESS; 01120 } 01121 01122 while ((Synchronize) && (ControlArea->FlushInProgressCount != 0)) { 01123 01124 // 01125 // Another thread is currently performing a flush operation on 01126 // this file. Wait for that flush to complete. 01127 // 01128 01129 KeEnterCriticalRegion(); 01130 ControlArea->u.Flags.CollidedFlush = 1; 01131 UNLOCK_PFN_AND_THEN_WAIT(OldIrql); 01132 01133 KeWaitForSingleObject (&MmCollidedFlushEvent, 01134 WrPageOut, 01135 KernelMode, 01136 FALSE, 01137 &MmOneSecond); 01138 LOCK_PFN (OldIrql); 01139 KeLeaveCriticalRegion(); 01140 } 01141 01142 ControlArea->FlushInProgressCount += 1; 01143 01144 for (;;) { 01145 01146 if (LastSubsection != Subsection) { 01147 01148 // 01149 // Flush to the last PTE in this subsection. 01150 // 01151 01152 LastPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection]; 01153 } else { 01154 01155 // 01156 // Flush to the end of the range. 01157 // 01158 01159 LastPte = FinalPte; 01160 } 01161 01162 // 01163 // If the prototype PTEs are paged out or have a share count 01164 // of 1, they cannot contain any transition or valid PTEs. 01165 // 01166 01167 if (!MiCheckProtoPtePageState(PointerPte, TRUE)) { 01168 PointerPte = (PMMPTE)(((ULONG_PTR)PointerPte | (PAGE_SIZE - 1)) + 1); 01169 } 01170 01171 while (PointerPte < LastPte) { 01172 01173 if (MiIsPteOnPdeBoundary(PointerPte)) { 01174 01175 // 01176 // We are on a page boundary, make sure this PTE is resident. 01177 // 01178 01179 if (!MiCheckProtoPtePageState(PointerPte, TRUE)) { 01180 PointerPte = (PMMPTE)((PCHAR)PointerPte + PAGE_SIZE); 01181 01182 // 01183 // If there are dirty pages to be written, write them 01184 // now as we are skipping over PTEs. 01185 // 01186 01187 if (LastWritten != NULL) { 01188 WriteNow = TRUE; 01189 goto CheckForWrite; 01190 } 01191 continue; 01192 } 01193 } 01194 01195 PteContents = *PointerPte; 01196 01197 if ((PteContents.u.Hard.Valid == 1) || 01198 ((PteContents.u.Soft.Prototype == 0) && 01199 (PteContents.u.Soft.Transition == 1))) { 01200 01201 // 01202 // Prototype PTE in transition, there are 3 possible cases: 01203 // 1. The page is part of an image which is sharable and 01204 // refers to the paging file - dereference page file 01205 // space and free the physical page. 01206 // 2. The page refers to the segment but is not modified - 01207 // free the physical page. 01208 // 3. The page refers to the segment and is modified - 01209 // write the page to the file and free the physical page. 01210 // 01211 01212 if (PteContents.u.Hard.Valid == 1) { 01213 PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents); 01214 } else { 01215 PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents); 01216 } 01217 01218 Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); 01219 ASSERT (Pfn1->OriginalPte.u.Soft.Prototype == 1); 01220 ASSERT (Pfn1->OriginalPte.u.Hard.Valid == 0); 01221 01222 // 01223 // If the page is modified OR a write is in progress 01224 // flush it. The write in progress case catches problems 01225 // where the modified page write continually writes a 01226 // page and gets errors writing it, by writing pages 01227 // in this state, the error will be propagated back to 01228 // the caller. 01229 // 01230 01231 if ((Pfn1->u3.e1.Modified == 1) || 01232 (Pfn1->u3.e1.WriteInProgress)) { 01233 01234 if ((WriteInProgressOk == FALSE) && 01235 (Pfn1->u3.e1.WriteInProgress)) { 01236 01237 PointerPte = LastPte; 01238 Bail = TRUE; 01239 01240 if (LastWritten != NULL) { 01241 WriteNow = TRUE; 01242 } 01243 goto CheckForWrite; 01244 } 01245 01246 if (LastWritten == NULL) { 01247 01248 // 01249 // This is the first page of a cluster, initialize 01250 // the MDL, etc. 01251 // 01252 01253 LastPage = (PPFN_NUMBER)(Mdl + 1); 01254 01255 // 01256 // Calculate the offset to read into the file. 01257 // offset = base + ((thispte - basepte) << PAGE_SHIFT) 01258 // 01259 01260 StartingOffset = (UINT64) MiStartingOffset ( 01261 Subsection, 01262 Pfn1->PteAddress); 01263 01264 MI_INITIALIZE_ZERO_MDL (Mdl); 01265 01266 Mdl->MdlFlags |= MDL_PAGES_LOCKED; 01267 Mdl->StartVa = 01268 (PVOID)ULongToPtr(Pfn1->u3.e1.PageColor << PAGE_SHIFT); 01269 Mdl->Size = (CSHORT)(sizeof(MDL) + 01270 (sizeof(PFN_NUMBER) * MmModifiedWriteClusterSize)); 01271 } 01272 01273 LastWritten = PointerPte; 01274 Mdl->ByteCount += PAGE_SIZE; 01275 if (Mdl->ByteCount == (PAGE_SIZE * MmModifiedWriteClusterSize)) { 01276 WriteNow = TRUE; 01277 } 01278 01279 if (PteContents.u.Hard.Valid == 0) { 01280 01281 // 01282 // The page is in transition. 01283 // 01284 01285 MiUnlinkPageFromList (Pfn1); 01286 MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE(Pfn1, 18); 01287 } 01288 else { 01289 MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, 20); 01290 } 01291 01292 // 01293 // Clear the modified bit for this page. 01294 // 01295 01296 Pfn1->u3.e1.Modified = 0; 01297 01298 // 01299 // Up the reference count for the physical page as there 01300 // is I/O in progress. 01301 // 01302 01303 Pfn1->u3.e2.ReferenceCount += 1; 01304 01305 *LastPage = PageFrameIndex; 01306 LastPage += 1; 01307 } else { 01308 01309 // 01310 // This page was not modified and therefore ends the 01311 // current write cluster if any. Set WriteNow to TRUE 01312 // if there is a cluster being built. 01313 // 01314 01315 if (LastWritten != NULL) { 01316 WriteNow = TRUE; 01317 } 01318 } 01319 } else { 01320 01321 // 01322 // This page was not modified and therefore ends the 01323 // current write cluster if any. Set WriteNow to TRUE 01324 // if there is a cluster being built. 01325 // 01326 01327 if (LastWritten != NULL) { 01328 WriteNow = TRUE; 01329 } 01330 } 01331 01332 PointerPte += 1; 01333 01334 CheckForWrite: 01335 01336 // 01337 // Write the current cluster if it is complete, 01338 // full, or the loop is now complete. 01339 // 01340 01341 if ((WriteNow) || 01342 ((PointerPte == LastPte) && (LastWritten != NULL))) { 01343 01344 LARGE_INTEGER EndOfFile; 01345 01346 // 01347 // Issue the write request. 01348 // 01349 01350 UNLOCK_PFN (OldIrql); 01351 01352 WriteNow = FALSE; 01353 01354 // 01355 // Make sure the write does not go past the 01356 // end of file. (segment size). 01357 // 01358 01359 EndOfFile = MiEndingOffset(Subsection); 01360 TempOffset = (UINT64) EndOfFile.QuadPart; 01361 01362 if (StartingOffset + Mdl->ByteCount > TempOffset) { 01363 01364 ASSERT ((ULONG_PTR)(TempOffset - StartingOffset) > 01365 (Mdl->ByteCount - PAGE_SIZE)); 01366 01367 Mdl->ByteCount = (ULONG)(TempOffset- 01368 StartingOffset); 01369 } 01370 01371 ReflushCount = MiIoRetryLevel; 01372 01373 while ( TRUE ) { 01374 01375 KeClearEvent (&IoEvent); 01376 01377 #if DBG 01378 if (MmDebug & MM_DBG_FLUSH_SECTION) { 01379 DbgPrint("flush page write begun %lx\n", 01380 Mdl->ByteCount); 01381 } 01382 #endif //DBG 01383 01384 Status = IoSynchronousPageWrite (ControlArea->FilePointer, 01385 Mdl, 01386 (PLARGE_INTEGER)&StartingOffset, 01387 &IoEvent, 01388 IoStatus ); 01389 01390 // 01391 // If success is returned, wait for the i/o event to be set. 01392 // 01393 01394 if (NT_SUCCESS(Status)) { 01395 KeWaitForSingleObject( &IoEvent, 01396 WrPageOut, 01397 KernelMode, 01398 FALSE, 01399 (PLARGE_INTEGER)NULL); 01400 // 01401 // Otherwise, copy the error to the IoStatus, for error 01402 // handling below. 01403 // 01404 01405 } else { 01406 IoStatus->Status = Status; 01407 } 01408 01409 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) { 01410 MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl); 01411 } 01412 01413 Page = (PPFN_NUMBER)(Mdl + 1); 01414 01415 if (MmIsRetryIoStatus(IoStatus->Status)) { 01416 01417 ReflushCount -= 1; 01418 if (ReflushCount != 0) { 01419 KeDelayExecutionThread (KernelMode, FALSE, &Mm30Milliseconds); 01420 continue; 01421 } 01422 } 01423 01424 break; 01425 } 01426 01427 LOCK_PFN (OldIrql); 01428 01429 if (MiIsPteOnPdeBoundary(PointerPte) == 0) { 01430 01431 // 01432 // The next PTE is not in a different page, make 01433 // sure this page did not leave memory when the 01434 // I/O was in progress. 01435 // 01436 01437 MiMakeSystemAddressValidPfn (PointerPte); 01438 } 01439 01440 if (NT_SUCCESS(IoStatus->Status)) { 01441 01442 // 01443 // The I/O completed successfully, unlock the pages. 01444 // 01445 01446 while (Page < LastPage) { 01447 01448 Pfn2 = MI_PFN_ELEMENT (*Page); 01449 MI_REMOVE_LOCKED_PAGE_CHARGE(Pfn2, 19); 01450 MiDecrementReferenceCount (*Page); 01451 Page += 1; 01452 } 01453 } else { 01454 01455 // 01456 // Don't count on the file system to convey anything on errors 01457 // in the information field. 01458 // 01459 01460 IoStatus->Information = 0; 01461 01462 // 01463 // The I/O completed unsuccessfully, unlock the pages 01464 // and return an error status. 01465 // 01466 01467 while (Page < LastPage) { 01468 01469 Pfn2 = MI_PFN_ELEMENT (*Page); 01470 01471 // 01472 // Mark the page dirty again so it can be rewritten. 01473 // 01474 01475 Pfn2->u3.e1.Modified = 1; 01476 01477 MI_REMOVE_LOCKED_PAGE_CHARGE(Pfn2, 21); 01478 MiDecrementReferenceCount (*Page); 01479 Page += 1; 01480 } 01481 01482 // 01483 // Calculate how much was written thus far 01484 // and add that to the information field 01485 // of the IOSB. 01486 // 01487 01488 IoStatus->Information += 01489 (((LastWritten - StartingPte) << PAGE_SHIFT) - 01490 Mdl->ByteCount); 01491 01492 goto ErrorReturn; 01493 } 01494 01495 // 01496 // As the PFN lock has been released and 01497 // reacquired, do this loop again as the 01498 // PTE may have changed state. 01499 // 01500 01501 LastWritten = NULL; 01502 } 01503 01504 } //end while 01505 01506 if ((Bail == TRUE) || (Subsection == LastSubsection)) { 01507 01508 // 01509 // The last range has been flushed or we have collided with the 01510 // mapped page writer. Regardless, exit the top FOR loop 01511 // and return. 01512 // 01513 01514 break; 01515 } 01516 01517 Subsection = Subsection->NextSubsection; 01518 PointerPte = Subsection->SubsectionBase; 01519 01520 } //end for 01521 01522 ASSERT (LastWritten == NULL); 01523 01524 ErrorReturn: 01525 01526 ControlArea->FlushInProgressCount -= 1; 01527 if ((ControlArea->u.Flags.CollidedFlush == 1) && 01528 (ControlArea->FlushInProgressCount == 0)) { 01529 ControlArea->u.Flags.CollidedFlush = 0; 01530 KePulseEvent (&MmCollidedFlushEvent, 0, FALSE); 01531 } 01532 UNLOCK_PFN (OldIrql); 01533 01534 if (Bail == TRUE) { 01535 01536 // 01537 // This routine collided with the mapped page writer and the caller 01538 // expects an error for this. Give it to him. 01539 // 01540 01541 return STATUS_MAPPED_WRITER_COLLISION; 01542 } 01543 01544 return IoStatus->Status; 01545 } 01546 01547 BOOLEAN 01548 MmPurgeSection ( 01549 IN PSECTION_OBJECT_POINTERS SectionObjectPointer, 01550 IN PLARGE_INTEGER Offset, 01551 IN SIZE_T RegionSize, 01552 IN ULONG IgnoreCacheViews 01553 ) 01554 01555 /*++ 01556 01557 Routine Description: 01558 01559 This function determines if any views of the specified section 01560 are mapped, and if not, purges valid pages (even modified ones) 01561 from the specified section and returns any used pages to the free 01562 list. This is accomplished by examining the prototype PTEs 01563 from the specified offset to the end of the section, and if 01564 any prototype PTEs are in the transition state, putting the 01565 prototype PTE back into its original state and putting the 01566 physical page on the free list. 01567 01568 NOTE: 01569 01570 If there is an I/O operation ongoing for one of the pages, 01571 that page is eliminated from the segment and allowed to "float" 01572 until the i/o is complete. Once the share count goes to zero 01573 the page will be added to the free page list. 01574 01575 Arguments: 01576 01577 SectionObjectPointer - Supplies a pointer to the section objects. 01578 01579 Offset - Supplies the offset into the section in which to begin 01580 purging pages. If this argument is not present, then the 01581 whole section is purged without regard to the region size 01582 argument. 01583 01584 01585 RegionSize - Supplies the size of the region to purge. If this 01586 is specified as zero and Offset is specified, the 01587 region from Offset to the end of the file is purged. 01588 01589 Note: The largest value acceptable for RegionSize is 01590 0xFFFF0000; 01591 01592 IgnoreCacheViews - Supplies FALSE if mapped views in the system 01593 cache should cause the function to return FALSE. 01594 This is the normal case. 01595 Supplies TRUE if mapped views should be ignored 01596 and the flush should occur. NOTE THAT IF TRUE 01597 IS SPECIFIED AND ANY DATA PURGED IS CURRENTLY MAPPED 01598 AND VALID A BUGCHECK WILL OCCUR!! 01599 01600 Return Value: 01601 01602 Returns TRUE if either no section exists for the file object or 01603 the section is not mapped and the purge was done, FALSE otherwise. 01604 01605 Note that FALSE is returned if during the purge operation, a page 01606 could not be purged due to a non-zero reference count. 01607 01608 --*/ 01609 01610 { 01611 PCONTROL_AREA ControlArea; 01612 PMMPTE PointerPte; 01613 PMMPTE LastPte; 01614 PMMPTE FinalPte; 01615 MMPTE PteContents; 01616 PMMPFN Pfn1; 01617 KIRQL OldIrql; 01618 ULONG PteOffset; 01619 PSUBSECTION Subsection; 01620 PSUBSECTION LastSubsection; 01621 LARGE_INTEGER LocalOffset; 01622 BOOLEAN DeleteSegment = FALSE; 01623 BOOLEAN LockHeld; 01624 BOOLEAN ReturnValue; 01625 PFN_NUMBER PageFrameIndex; 01626 #if DBG 01627 PFN_NUMBER LastLocked = 0; 01628 #endif //DBG 01629 01630 // 01631 // This is needed in case a page is on the mapped page writer list - 01632 // the PFN lock will need to be released and APCs disabled. 01633 // 01634 01635 ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL); 01636 01637 // 01638 // Capture caller's file size, since we may modify it. 01639 // 01640 01641 if (ARGUMENT_PRESENT(Offset)) { 01642 01643 LocalOffset = *Offset; 01644 Offset = &LocalOffset; 01645 } 01646 01647 // 01648 // See if we can truncate this file to where the caller wants 01649 // us to. 01650 // 01651 01652 if (!MiCanFileBeTruncatedInternal(SectionObjectPointer, Offset, TRUE, &OldIrql)) { 01653 return FALSE; 01654 } 01655 01656 // 01657 // PFN LOCK IS NOW HELD! 01658 // 01659 01660 ControlArea = (PCONTROL_AREA)(SectionObjectPointer->DataSectionObject); 01661 if (ControlArea == NULL) { 01662 UNLOCK_PFN (OldIrql); 01663 return TRUE; 01664 01665 // 01666 // Even though MiCanFileBeTruncatedInternal returned TRUE, there could 01667 // still be a system cache mapped view. We cannot truncate if 01668 // the Cache Manager has a view mapped. 01669 // 01670 01671 } else if ((IgnoreCacheViews == FALSE) && 01672 (ControlArea->NumberOfSystemCacheViews != 0)) { 01673 UNLOCK_PFN (OldIrql); 01674 return FALSE; 01675 } 01676 01677 // 01678 // Prevent races when the control area is being deleted as the clean 01679 // path releases the PFN lock midway through. File objects may still have 01680 // section object pointers and data section objects that point at this 01681 // control area, hence the purge can be issued. 01682 // 01683 // Check for this and fail the purge as the control area (and the section 01684 // object pointers/data section objects) will be going away momentarily. 01685 // Note that even though drivers have these data section objects, no one 01686 // currently has an open section for this control area and no one is 01687 // allowed to open one until the clean path finishes. 01688 // 01689 01690 if (ControlArea->u.Flags.BeingDeleted == 1) { 01691 UNLOCK_PFN (OldIrql); 01692 return FALSE; 01693 } 01694 01695 // 01696 // Purge the section - locate the subsection which 01697 // contains the PTEs. 01698 // 01699 01700 ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0); 01701 01702 Subsection = (PSUBSECTION)(ControlArea + 1); 01703 01704 if (!ARGUMENT_PRESENT (Offset)) { 01705 01706 // 01707 // If the offset is not specified, flush the complete file ignoring 01708 // the region size. 01709 // 01710 01711 PointerPte = &Subsection->SubsectionBase[0]; 01712 RegionSize = 0; 01713 01714 } else { 01715 01716 PteOffset = (ULONG)(Offset->QuadPart >> PAGE_SHIFT); 01717 01718 // 01719 // Make sure the PTEs are not in the extended part of the 01720 // segment. 01721 // 01722 01723 while (PteOffset >= Subsection->PtesInSubsection) { 01724 PteOffset -= Subsection->PtesInSubsection; 01725 Subsection = Subsection->NextSubsection; 01726 if (Subsection == NULL) { 01727 01728 // 01729 // The offset must be equal to the size of 01730 // the section, don't purge anything just return. 01731 // 01732 01733 //ASSERT (PteOffset == 0); 01734 UNLOCK_PFN (OldIrql); 01735 return TRUE; 01736 } 01737 } 01738 01739 ASSERT (PteOffset < Subsection->PtesInSubsection); 01740 PointerPte = &Subsection->SubsectionBase[PteOffset]; 01741 } 01742 01743 01744 // 01745 // Locate the address of the last prototype PTE to be flushed. 01746 // 01747 01748 if (RegionSize == 0) { 01749 01750 // 01751 // Flush to end of section. 01752 // 01753 01754 LastSubsection = Subsection; 01755 while (LastSubsection->NextSubsection != NULL) { 01756 LastSubsection = LastSubsection->NextSubsection; 01757 } 01758 01759 // 01760 // Set the final PTE to 1 beyond the last page. 01761 // 01762 01763 FinalPte = &LastSubsection->SubsectionBase 01764 [LastSubsection->PtesInSubsection]; 01765 } else { 01766 01767 // 01768 // Calculate the end of the region. 01769 // 01770 01771 PteOffset += 01772 (ULONG) (((RegionSize + BYTE_OFFSET(Offset->LowPart)) - 1) >> PAGE_SHIFT); 01773 01774 LastSubsection = Subsection; 01775 01776 while (PteOffset >= LastSubsection->PtesInSubsection) { 01777 PteOffset -= LastSubsection->PtesInSubsection; 01778 if (LastSubsection->NextSubsection == NULL) { 01779 PteOffset = LastSubsection->PtesInSubsection - 1; 01780 break; 01781 } 01782 LastSubsection = LastSubsection->NextSubsection; 01783 } 01784 01785 ASSERT (PteOffset < LastSubsection->PtesInSubsection); 01786 01787 // 01788 // Point final PTE to 1 beyond the end. 01789 // 01790 01791 FinalPte = &LastSubsection->SubsectionBase[PteOffset + 1]; 01792 } 01793 01794 // 01795 // Increment the number of mapped views to 01796 // prevent the section from being deleted while the purge is 01797 // in progress. 01798 // 01799 01800 ControlArea->NumberOfMappedViews += 1; 01801 01802 // 01803 // Set being purged so no one can map a view 01804 // while the purge is going on. 01805 // 01806 01807 ControlArea->u.Flags.BeingPurged = 1; 01808 ControlArea->u.Flags.WasPurged = 1; 01809 01810 UNLOCK_PFN (OldIrql); 01811 LockHeld = FALSE; 01812 ReturnValue = TRUE; 01813 01814 for (;;) { 01815 01816 if (LastSubsection != Subsection) { 01817 01818 // 01819 // Flush to the last PTE in this subsection. 01820 // 01821 01822 LastPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection]; 01823 } else { 01824 01825 // 01826 // Flush to the end of the range. 01827 // 01828 01829 LastPte = FinalPte; 01830 } 01831 01832 // 01833 // If the page table page containing the PTEs is not 01834 // resident, then no PTEs can be in the valid or transition 01835 // state! Skip over the PTEs. 01836 // 01837 01838 if (!MiCheckProtoPtePageState(PointerPte, LockHeld)) { 01839 PointerPte = (PMMPTE)(((ULONG_PTR)PointerPte | (PAGE_SIZE - 1)) + 1); 01840 } 01841 01842 while (PointerPte < LastPte) { 01843 01844 // 01845 // If the page table page containing the PTEs is not 01846 // resident, then no PTEs can be in the valid or transition 01847 // state! Skip over the PTEs. 01848 // 01849 01850 if (MiIsPteOnPdeBoundary(PointerPte)) { 01851 if (!MiCheckProtoPtePageState(PointerPte, LockHeld)) { 01852 PointerPte = (PMMPTE)((PCHAR)PointerPte + PAGE_SIZE); 01853 continue; 01854 } 01855 } 01856 01857 PteContents = *PointerPte; 01858 01859 if (PteContents.u.Hard.Valid == 1) { 01860 01861 // 01862 // A valid PTE was found, it must be mapped in the 01863 // system cache. Just exit the loop and return FALSE 01864 // and let the caller fix this. 01865 // 01866 01867 ReturnValue = FALSE; 01868 break; 01869 } 01870 01871 if ((PteContents.u.Soft.Prototype == 0) && 01872 (PteContents.u.Soft.Transition == 1)) { 01873 01874 if (!LockHeld) { 01875 LockHeld = TRUE; 01876 LOCK_PFN (OldIrql); 01877 MiMakeSystemAddressValidPfn (PointerPte); 01878 continue; 01879 } 01880 01881 PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE(&PteContents); 01882 Pfn1 = MI_PFN_ELEMENT (PageFrameIndex); 01883 01884 if ((Pfn1->OriginalPte.u.Soft.Prototype != 1) || 01885 (Pfn1->OriginalPte.u.Hard.Valid != 0) || 01886 (Pfn1->PteAddress != PointerPte)) { 01887 01888 // 01889 // The pool containing the prototype PTEs has been 01890 // corrupted. Pool corruption like this is fatal. 01891 // 01892 01893 KeBugCheckEx (POOL_CORRUPTION_IN_FILE_AREA, 01894 0x2, 01895 (ULONG_PTR)PointerPte, 01896 (ULONG_PTR)Pfn1->PteAddress, 01897 (ULONG_PTR)PteContents.u.Long); 01898 } 01899 01900 #if DBG 01901 if ((Pfn1->u3.e2.ReferenceCount != 0) && 01902 (Pfn1->u3.e1.WriteInProgress == 0)) { 01903 01904 // 01905 // There must be an I/O in progress on this 01906 // page. 01907 // 01908 01909 if (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE(&PteContents) != LastLocked) { 01910 UNLOCK_PFN (OldIrql); 01911 01912 #if DBG 01913 if (MmDebug & MM_DBG_FLUSH_SECTION) { 01914 DbgPrint("MM:PURGE - page %lx locked, file:%Z\n", 01915 PageFrameIndex, 01916 &ControlArea->FilePointer->FileName 01917 ); 01918 } 01919 #endif 01920 LastLocked = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents); 01921 //DbgBreakPoint(); 01922 LOCK_PFN (OldIrql); 01923 MiMakeSystemAddressValidPfn (PointerPte); 01924 continue; 01925 } 01926 } 01927 #endif //DBG 01928 01929 // 01930 // If the modified page writer has page locked for I/O 01931 // wait for the I/O's to be completed and the pages 01932 // to be unlocked. The eliminates a race condition 01933 // when the modified page writer locks the pages, then 01934 // a purge occurs and completes before the mapped 01935 // writer thread runs. 01936 // 01937 01938 if (Pfn1->u3.e1.WriteInProgress == 1) { 01939 01940 // 01941 // A 3 or more thread deadlock can occur where: 01942 // 01943 // 1. The mapped page writer thread has issued a write 01944 // and is in the filesystem code waiting for a resource. 01945 // 01946 // 2. Thread 2 owns the resource above but is waiting for 01947 // the filesystem's quota mutex. 01948 // 01949 // 3. Thread 3 owns the quota mutex and is right here 01950 // doing a purge from the cache manager when he notices 01951 // the page to be purged is either already being written 01952 // or is in the mapped page writer list. If it is 01953 // already being written everything will unjam. If it 01954 // is still on the mapped page writer list awaiting 01955 // processing, then it must be cancelled - otherwise 01956 // if this thread were to wait, deadlock can occur. 01957 // 01958 // The alternative to all this is for the filesystems to 01959 // always release the quota mutex before purging but the 01960 // filesystem overhead to do this is substantial. 01961 // 01962 01963 if (MiCancelWriteOfMappedPfn (PageFrameIndex) == TRUE) { 01964 01965 // 01966 // Stopping any failed writes (even deliberately 01967 // cancelled ones) automatically cause a delay. A 01968 // successful stop also results in the PFN lock 01969 // being released and reacquired. So loop back to 01970 // the top now as the world may have changed. 01971 // 01972 01973 MiMakeSystemAddressValidPfn (PointerPte); 01974 continue; 01975 } 01976 01977 ASSERT (ControlArea->ModifiedWriteCount != 0); 01978 ASSERT (Pfn1->u3.e2.ReferenceCount != 0); 01979 01980 ControlArea->u.Flags.SetMappedFileIoComplete = 1; 01981 01982 KeEnterCriticalRegion(); 01983 UNLOCK_PFN_AND_THEN_WAIT(OldIrql); 01984 01985 KeWaitForSingleObject(&MmMappedFileIoComplete, 01986 WrPageOut, 01987 KernelMode, 01988 FALSE, 01989 (PLARGE_INTEGER)NULL); 01990 LOCK_PFN (OldIrql); 01991 KeLeaveCriticalRegion(); 01992 MiMakeSystemAddressValidPfn (PointerPte); 01993 continue; 01994 } 01995 01996 if (Pfn1->u3.e1.ReadInProgress == 1) { 01997 01998 // 01999 // The page currently is being read in from the 02000 // disk. Treat this just like a valid PTE and 02001 // return false. 02002 // 02003 02004 ReturnValue = FALSE; 02005 break; 02006 } 02007 02008 ASSERT (!((Pfn1->OriginalPte.u.Soft.Prototype == 0) && 02009 (Pfn1->OriginalPte.u.Soft.Transition == 1))); 02010 02011 MI_WRITE_INVALID_PTE (PointerPte, Pfn1->OriginalPte); 02012 02013 ASSERT (Pfn1->OriginalPte.u.Hard.Valid == 0); 02014 02015 ControlArea->NumberOfPfnReferences -= 1; 02016 ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0); 02017 02018 MiUnlinkPageFromList (Pfn1); 02019 02020 MI_SET_PFN_DELETED (Pfn1); 02021 02022 MiDecrementShareCount (Pfn1->PteFrame); 02023 02024 // 02025 // If the reference count for the page is zero, insert 02026 // it into the free page list, otherwise leave it alone 02027 // and when the reference count is decremented to zero 02028 // the page will go to the free list. 02029 // 02030 02031 if (Pfn1->u3.e2.ReferenceCount == 0) { 02032 MiReleasePageFileSpace (Pfn1->OriginalPte); 02033 MiInsertPageInList (MmPageLocationList[FreePageList], 02034 MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents)); 02035 } 02036 } 02037 PointerPte += 1; 02038 02039 if ((MiIsPteOnPdeBoundary(PointerPte)) && (LockHeld)) { 02040 02041 // 02042 // Unlock PFN so large requests will not block other 02043 // threads on MP systems. 02044 // 02045 02046 UNLOCK_PFN (OldIrql); 02047 LockHeld = FALSE; 02048 } 02049 02050 } //end while 02051 02052 if (LockHeld) { 02053 UNLOCK_PFN (OldIrql); 02054 LockHeld = FALSE; 02055 } 02056 02057 if ((LastSubsection != Subsection) && (ReturnValue)) { 02058 02059 // 02060 // Get the next subsection in the list. 02061 // 02062 02063 Subsection = Subsection->NextSubsection; 02064 PointerPte = Subsection->SubsectionBase; 02065 02066 } else { 02067 02068 // 02069 // The last range has been flushed, exit the top FOR loop 02070 // and return. 02071 // 02072 02073 break; 02074 } 02075 } //end for 02076 02077 LOCK_PFN (OldIrql); 02078 02079 ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1); 02080 ControlArea->NumberOfMappedViews -= 1; 02081 02082 ControlArea->u.Flags.BeingPurged = 0; 02083 02084 // 02085 // Check to see if the control area should be deleted. This 02086 // will release the PFN lock. 02087 // 02088 02089 MiCheckControlArea (ControlArea, NULL, OldIrql); 02090 return ReturnValue; 02091 } 02092 02093 BOOLEAN 02094 MmFlushImageSection ( 02095 IN PSECTION_OBJECT_POINTERS SectionPointer, 02096 IN MMFLUSH_TYPE FlushType 02097 ) 02098 02099 /*++ 02100 02101 Routine Description: 02102 02103 This function determines if any views of the specified image section 02104 are mapped, and if not, flushes valid pages (even modified ones) 02105 from the specified section and returns any used pages to the free 02106 list. This is accomplished by examining the prototype PTEs 02107 from the specified offset to the end of the section, and if 02108 any prototype PTEs are in the transition state, putting the 02109 prototype PTE back into its original state and putting the 02110 physical page on the free list. 02111 02112 Arguments: 02113 02114 SectionPointer - Supplies a pointer to a section object pointers 02115 within the FCB. 02116 02117 FlushType - Supplies the type of flush to check for. One of 02118 MmFlushForDelete or MmFlushForWrite. 02119 02120 Return Value: 02121 02122 Returns TRUE if either no section exists for the file object or 02123 the section is not mapped and the purge was done, FALSE otherwise. 02124 02125 --*/ 02126 02127 { 02128 PLIST_ENTRY Next; 02129 PCONTROL_AREA ControlArea; 02130 PLARGE_CONTROL_AREA LargeControlArea; 02131 KIRQL OldIrql; 02132 BOOLEAN state; 02133 BOOLEAN FinalControlArea; 02134 02135 02136 if (FlushType == MmFlushForDelete) { 02137 02138 // 02139 // Do a quick check to see if there are any mapped views for 02140 // the data section. If so, just return FALSE. 02141 // 02142 02143 LOCK_PFN (OldIrql); 02144 ControlArea = (PCONTROL_AREA)(SectionPointer->DataSectionObject); 02145 if (ControlArea != NULL) { 02146 if ((ControlArea->NumberOfUserReferences != 0) || 02147 (ControlArea->u.Flags.BeingCreated)) { 02148 UNLOCK_PFN (OldIrql); 02149 return FALSE; 02150 } 02151 } 02152 UNLOCK_PFN (OldIrql); 02153 } 02154 02155 // 02156 // Check the status of the control area. If the control area is in use 02157 // or the control area is being deleted, this operation cannot continue. 02158 // 02159 02160 state = MiCheckControlAreaStatus (CheckImageSection, 02161 SectionPointer, 02162 FALSE, 02163 &ControlArea, 02164 &OldIrql); 02165 02166 if (ControlArea == NULL) { 02167 return state; 02168 } 02169 02170 // 02171 // PFN LOCK IS NOW HELD! 02172 // 02173 02174 // 02175 // Repeat until there are no more control areas - multiple control areas 02176 // for the same image section occur to support user global DLLs - these DLLs 02177 // require data that is shared within a session but not across sessions. 02178 // Note this can only happen for Hydra. 02179 // 02180 02181 do { 02182 02183 // 02184 // Set the being deleted flag and up the number of mapped views 02185 // for the segment. Upping the number of mapped views prevents 02186 // the segment from being deleted and passed to the deletion thread 02187 // while we are forcing a delete. 02188 // 02189 02190 ControlArea->u.Flags.BeingDeleted = 1; 02191 ControlArea->NumberOfMappedViews = 1; 02192 FinalControlArea = TRUE; 02193 02194 if (MiHydra == FALSE) { 02195 ASSERT (ControlArea == 02196 (PCONTROL_AREA)SectionPointer->ImageSectionObject); 02197 } 02198 02199 else if (ControlArea->u.Flags.GlobalOnlyPerSession == 0) { 02200 } 02201 else if (IsListEmpty(&((PLARGE_CONTROL_AREA)ControlArea)->UserGlobalList)) { 02202 ASSERT (ControlArea == 02203 (PCONTROL_AREA)SectionPointer->ImageSectionObject); 02204 } 02205 else { 02206 02207 // 02208 // Check if there's only one image section in this control area, so 02209 // we don't reference the section object pointers as the 02210 // MiCleanSection call may result in its deletion. 02211 // 02212 02213 FinalControlArea = FALSE; 02214 02215 // 02216 // There are multiple control areas, bump the reference count 02217 // on one of them (doesn't matter which one) so that it can't 02218 // go away. This ensures the section object pointers will stick 02219 // around even after the calls below so we can safely reloop to 02220 // flush any other remaining control areas. 02221 // 02222 02223 ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 1); 02224 02225 Next = ((PLARGE_CONTROL_AREA)ControlArea)->UserGlobalList.Flink; 02226 02227 LargeControlArea = CONTAINING_RECORD (Next, 02228 LARGE_CONTROL_AREA, 02229 UserGlobalList); 02230 02231 ASSERT (LargeControlArea->u.Flags.GlobalOnlyPerSession == 1); 02232 02233 LargeControlArea->NumberOfSectionReferences += 1; 02234 } 02235 02236 // 02237 // This is a page file backed or image segment. The segment is being 02238 // deleted, remove all references to the paging file and physical 02239 // memory. 02240 // 02241 02242 UNLOCK_PFN (OldIrql); 02243 02244 MiCleanSection (ControlArea, TRUE); 02245 02246 // 02247 // Get the next Hydra control area. 02248 // 02249 02250 if (FinalControlArea == FALSE) { 02251 state = MiCheckControlAreaStatus (CheckImageSection, 02252 SectionPointer, 02253 FALSE, 02254 &ControlArea, 02255 &OldIrql); 02256 if (!ControlArea) { 02257 LOCK_PFN (OldIrql); 02258 LargeControlArea->NumberOfSectionReferences -= 1; 02259 MiCheckControlArea ((PCONTROL_AREA)LargeControlArea, 02260 NULL, 02261 OldIrql); 02262 } 02263 else { 02264 LargeControlArea->NumberOfSectionReferences -= 1; 02265 MiCheckControlArea ((PCONTROL_AREA)LargeControlArea, 02266 NULL, 02267 OldIrql); 02268 LOCK_PFN (OldIrql); 02269 } 02270 } else { 02271 state = TRUE; 02272 break; 02273 } 02274 02275 } while (ControlArea); 02276 02277 return state; 02278 } 02279 02280 VOID 02281 MiFlushDirtyBitsToPfn ( 02282 IN PMMPTE PointerPte, 02283 IN PMMPTE LastPte, 02284 IN PEPROCESS Process, 02285 IN BOOLEAN SystemCache 02286 ) 02287 02288 { 02289 KIRQL OldIrql; 02290 MMPTE PteContents; 02291 PMMPFN Pfn1; 02292 PVOID Va; 02293 PMMPTE PointerPde; 02294 PMMPTE PointerPpe; 02295 ULONG Waited; 02296 02297 Va = MiGetVirtualAddressMappedByPte (PointerPte); 02298 LOCK_PFN (OldIrql); 02299 02300 while (PointerPte <= LastPte) { 02301 02302 PteContents = *PointerPte; 02303 02304 if ((PteContents.u.Hard.Valid == 1) && 02305 (MI_IS_PTE_DIRTY (PteContents))) { 02306 02307 // 02308 // Flush the modify bit to the PFN database. 02309 // 02310 02311 Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber); 02312 Pfn1->u3.e1.Modified = 1; 02313 02314 MI_SET_PTE_CLEAN (PteContents); 02315 02316 // 02317 // No need to capture the PTE contents as we are going to 02318 // write the page anyway and the Modify bit will be cleared 02319 // before the write is done. 02320 // 02321 02322 (VOID)KeFlushSingleTb (Va, 02323 FALSE, 02324 SystemCache, 02325 (PHARDWARE_PTE)PointerPte, 02326 PteContents.u.Flush); 02327 } 02328 02329 Va = (PVOID)((PCHAR)Va + PAGE_SIZE); 02330 PointerPte += 1; 02331 02332 if (MiIsPteOnPdeBoundary (PointerPte)) { 02333 02334 PointerPde = MiGetPteAddress (PointerPte); 02335 02336 while (PointerPte <= LastPte) { 02337 02338 PointerPpe = MiGetPteAddress (PointerPde); 02339 02340 if (!MiDoesPpeExistAndMakeValid (PointerPpe, 02341 Process, 02342 TRUE, 02343 &Waited)) { 02344 02345 // 02346 // No page directory page exists for this address. 02347 // 02348 02349 PointerPpe += 1; 02350 02351 PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe); 02352 PointerPte = MiGetVirtualAddressMappedByPte (PointerPde); 02353 } 02354 else { 02355 02356 Waited = 0; 02357 02358 if (!MiDoesPdeExistAndMakeValid (PointerPde, 02359 Process, 02360 TRUE, 02361 &Waited)) { 02362 02363 // 02364 // No page table page exists for this address. 02365 // 02366 02367 PointerPde += 1; 02368 02369 PointerPte = MiGetVirtualAddressMappedByPte (PointerPde); 02370 } 02371 else { 02372 02373 // 02374 // If the PFN lock (and accordingly the WS mutex) was 02375 // released and reacquired we must retry the operation. 02376 // 02377 02378 if (Waited != 0) { 02379 continue; 02380 } 02381 02382 // 02383 // The PFN lock has been held since we acquired the 02384 // page directory parent, ie: this PTE we can operate on 02385 // immediately. 02386 // 02387 02388 break; 02389 } 02390 } 02391 } 02392 02393 Va = MiGetVirtualAddressMappedByPte (PointerPte); 02394 } 02395 } 02396 02397 UNLOCK_PFN (OldIrql); 02398 return; 02399 } 02400 02401 PSUBSECTION 02402 MiGetSystemCacheSubsection ( 02403 IN PVOID BaseAddress, 02404 IN PEPROCESS Process, 02405 OUT PMMPTE *ProtoPte 02406 ) 02407 02408 { 02409 KIRQL OldIrql; 02410 PMMPTE PointerPte; 02411 PSUBSECTION Subsection; 02412 02413 LOCK_PFN (OldIrql); 02414 02415 PointerPte = MiGetPteAddress (BaseAddress); 02416 02417 Subsection = MiGetSubsectionAndProtoFromPte (PointerPte, 02418 ProtoPte, 02419 Process); 02420 UNLOCK_PFN (OldIrql); 02421 return Subsection; 02422 } 02423 02424 02425 ULONG 02426 FASTCALL 02427 MiCheckProtoPtePageState ( 02428 IN PMMPTE PrototypePte, 02429 IN ULONG PfnLockHeld 02430 ) 02431 02432 /*++ 02433 02434 Routine Description: 02435 02436 Checks the state of the page containing the specified 02437 prototype PTE. 02438 02439 If the page is valid or transition and has transition or valid prototype 02440 PTEs contained with it, TRUE is returned and the page is made valid 02441 (if transition). Otherwise return FALSE indicating no prototype 02442 PTEs within this page are of interest. 02443 02444 Arguments: 02445 02446 PrototypePte - Supplies a pointer to a prototype PTE within the page. 02447 02448 Return Value: 02449 02450 TRUE if the page containing the proto PTE was made resident. 02451 FALSE if otherwise. 02452 02453 --*/ 02454 02455 { 02456 PMMPTE PointerPte; 02457 MMPTE PteContents; 02458 PFN_NUMBER PageFrameIndex; 02459 PMMPFN Pfn; 02460 02461 #if defined (_WIN64) 02462 // 02463 // First check whether the page directory page is present. Since there 02464 // is no lazy loading of PPEs, the validity check alone is sufficient. 02465 // 02466 02467 PointerPte = MiGetPdeAddress(PrototypePte); 02468 PteContents = *PointerPte; 02469 02470 if (PteContents.u.Hard.Valid == 0) { 02471 return FALSE; 02472 } 02473 #endif 02474 02475 PointerPte = MiGetPteAddress(PrototypePte); 02476 #if !defined (_WIN64) 02477 if (PointerPte->u.Hard.Valid == 0) { 02478 MiCheckPdeForPagedPool (PrototypePte); 02479 } 02480 #endif 02481 PteContents = *PointerPte; 02482 02483 if (PteContents.u.Hard.Valid == 1) { 02484 PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents); 02485 Pfn = MI_PFN_ELEMENT (PageFrameIndex); 02486 if (Pfn->u2.ShareCount != 1) { 02487 return TRUE; 02488 } 02489 } else if ((PteContents.u.Soft.Prototype == 0) && 02490 (PteContents.u.Soft.Transition == 1)) { 02491 02492 // 02493 // Transition, if on standby or modified, return false. 02494 // 02495 02496 PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents); 02497 Pfn = MI_PFN_ELEMENT (PageFrameIndex); 02498 if (Pfn->u3.e1.PageLocation >= ActiveAndValid) { 02499 if (PfnLockHeld) { 02500 MiMakeSystemAddressValidPfn (PrototypePte); 02501 } 02502 return TRUE; 02503 } 02504 } 02505 02506 // 02507 // Page is not resident or is on standby / modified list. 02508 // 02509 02510 return FALSE; 02511 }

Generated on Sat May 15 19:40:02 2004 for test by doxygen 1.3.7