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.

3152 lines
91 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. flushsec.c
  5. Abstract:
  6. This module contains the routines which implement the
  7. NtFlushVirtualMemory service.
  8. Author:
  9. Lou Perazzoli (loup) 8-May-1990
  10. Landy Wang (landyw) 02-June-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. PSUBSECTION
  15. MiGetSystemCacheSubsection (
  16. IN PVOID BaseAddress,
  17. OUT PMMPTE *ProtoPte
  18. );
  19. VOID
  20. MiFlushDirtyBitsToPfn (
  21. IN PMMPTE PointerPte,
  22. IN PMMPTE LastPte,
  23. IN PEPROCESS Process,
  24. IN BOOLEAN SystemCache
  25. );
  26. #ifdef ALLOC_PRAGMA
  27. #pragma alloc_text(PAGE,NtFlushVirtualMemory)
  28. #pragma alloc_text(PAGE,MmFlushVirtualMemory)
  29. #endif
  30. extern POBJECT_TYPE IoFileObjectType;
  31. NTSTATUS
  32. NtFlushVirtualMemory (
  33. IN HANDLE ProcessHandle,
  34. IN OUT PVOID *BaseAddress,
  35. IN OUT PSIZE_T RegionSize,
  36. OUT PIO_STATUS_BLOCK IoStatus
  37. )
  38. /*++
  39. Routine Description:
  40. This function flushes a range of virtual address which map
  41. a data file back into the data file if they have been modified.
  42. Arguments:
  43. ProcessHandle - Supplies an open handle to a process object.
  44. BaseAddress - Supplies a pointer to a variable that will receive
  45. the base address the flushed region. The initial value
  46. of this argument is the base address of the region of the
  47. pages to flush.
  48. RegionSize - Supplies a pointer to a variable that will receive
  49. the actual size in bytes of the flushed region of pages.
  50. The initial value of this argument is rounded up to the
  51. next host-page-size boundary.
  52. If this value is specified as zero, the mapped range from
  53. the base address to the end of the range is flushed.
  54. IoStatus - Returns the value of the IoStatus for the last attempted
  55. I/O operation.
  56. Return Value:
  57. Returns the status
  58. TBS
  59. --*/
  60. {
  61. PEPROCESS Process;
  62. KPROCESSOR_MODE PreviousMode;
  63. NTSTATUS Status;
  64. PVOID CapturedBase;
  65. SIZE_T CapturedRegionSize;
  66. IO_STATUS_BLOCK TemporaryIoStatus;
  67. PAGED_CODE();
  68. PreviousMode = KeGetPreviousMode();
  69. if (PreviousMode != KernelMode) {
  70. //
  71. // Establish an exception handler, probe the specified addresses
  72. // for write access and capture the initial values.
  73. //
  74. try {
  75. ProbeForWritePointer (BaseAddress);
  76. ProbeForWriteUlong_ptr (RegionSize);
  77. ProbeForWriteIoStatus (IoStatus);
  78. //
  79. // Capture the base address.
  80. //
  81. CapturedBase = *BaseAddress;
  82. //
  83. // Capture the region size.
  84. //
  85. CapturedRegionSize = *RegionSize;
  86. } except (EXCEPTION_EXECUTE_HANDLER) {
  87. //
  88. // If an exception occurs during the probe or capture
  89. // of the initial values, then handle the exception and
  90. // return the exception code as the status value.
  91. //
  92. return GetExceptionCode();
  93. }
  94. }
  95. else {
  96. //
  97. // Capture the base address.
  98. //
  99. CapturedBase = *BaseAddress;
  100. //
  101. // Capture the region size.
  102. //
  103. CapturedRegionSize = *RegionSize;
  104. }
  105. //
  106. // Make sure the specified starting and ending addresses are
  107. // within the user part of the virtual address space.
  108. //
  109. if (CapturedBase > MM_HIGHEST_USER_ADDRESS) {
  110. //
  111. // Invalid base address.
  112. //
  113. return STATUS_INVALID_PARAMETER_2;
  114. }
  115. if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)CapturedBase) <
  116. CapturedRegionSize) {
  117. //
  118. // Invalid region size;
  119. //
  120. return STATUS_INVALID_PARAMETER_2;
  121. }
  122. Status = ObReferenceObjectByHandle ( ProcessHandle,
  123. PROCESS_VM_OPERATION,
  124. PsProcessType,
  125. PreviousMode,
  126. (PVOID *)&Process,
  127. NULL );
  128. if (!NT_SUCCESS(Status)) {
  129. return Status;
  130. }
  131. Status = MmFlushVirtualMemory (Process,
  132. &CapturedBase,
  133. &CapturedRegionSize,
  134. &TemporaryIoStatus);
  135. ObDereferenceObject (Process);
  136. //
  137. // Establish an exception handler and write the size and base
  138. // address.
  139. //
  140. try {
  141. *RegionSize = CapturedRegionSize;
  142. *BaseAddress = PAGE_ALIGN (CapturedBase);
  143. *IoStatus = TemporaryIoStatus;
  144. } except (EXCEPTION_EXECUTE_HANDLER) {
  145. }
  146. return Status;
  147. }
  148. VOID
  149. MiFlushAcquire (
  150. IN PCONTROL_AREA ControlArea
  151. )
  152. /*++
  153. Routine Description:
  154. This is a helper routine to reference count the control area if needed
  155. during a flush section call to prevent the section object from being
  156. deleted while the flush is ongoing.
  157. Arguments:
  158. ControlArea - Supplies a pointer to the control area.
  159. Return Value:
  160. None.
  161. --*/
  162. {
  163. KIRQL OldIrql;
  164. LOCK_PFN (OldIrql);
  165. ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1);
  166. ControlArea->NumberOfMappedViews += 1;
  167. UNLOCK_PFN (OldIrql);
  168. }
  169. VOID
  170. MiFlushRelease (
  171. IN PCONTROL_AREA ControlArea
  172. )
  173. /*++
  174. Routine Description:
  175. This is a helper routine to release the control area reference needed
  176. during a flush section call.
  177. Arguments:
  178. ControlArea - Supplies a pointer to the control area.
  179. Return Value:
  180. None.
  181. --*/
  182. {
  183. KIRQL OldIrql;
  184. LOCK_PFN (OldIrql);
  185. ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1);
  186. ControlArea->NumberOfMappedViews -= 1;
  187. //
  188. // Check to see if the control area should be deleted. This
  189. // will release the PFN lock.
  190. //
  191. MiCheckControlArea (ControlArea, NULL, OldIrql);
  192. }
  193. NTSTATUS
  194. MmFlushVirtualMemory (
  195. IN PEPROCESS Process,
  196. IN OUT PVOID *BaseAddress,
  197. IN OUT PSIZE_T RegionSize,
  198. OUT PIO_STATUS_BLOCK IoStatus
  199. )
  200. /*++
  201. Routine Description:
  202. This function flushes a range of virtual address which map
  203. a data file back into the data file if they have been modified.
  204. Note that the modification is this process's view of the pages,
  205. on certain implementations (like the Intel 386), the modify
  206. bit is captured in the PTE and not forced to the PFN database
  207. until the page is removed from the working set. This means
  208. that pages which have been modified by another process will
  209. not be flushed to the data file.
  210. Arguments:
  211. Process - Supplies a pointer to a process object.
  212. BaseAddress - Supplies a pointer to a variable that will receive
  213. the base address of the flushed region. The initial value
  214. of this argument is the base address of the region of the
  215. pages to flush.
  216. RegionSize - Supplies a pointer to a variable that will receive
  217. the actual size in bytes of the flushed region of pages.
  218. The initial value of this argument is rounded up to the
  219. next host-page-size boundary.
  220. If this value is specified as zero, the mapped range from
  221. the base address to the end of the range is flushed.
  222. IoStatus - Returns the value of the IoStatus for the last attempted
  223. I/O operation.
  224. Return Value:
  225. NTSTATUS.
  226. --*/
  227. {
  228. PMMVAD Vad;
  229. PVOID EndingAddress;
  230. PVOID Va;
  231. PEPROCESS CurrentProcess;
  232. BOOLEAN SystemCache;
  233. PCONTROL_AREA ControlArea;
  234. PMMPTE PointerPte;
  235. PMMPTE PointerPde;
  236. PMMPTE PointerPpe;
  237. PMMPTE PointerPxe;
  238. PMMPTE LastPte;
  239. PMMPTE FinalPte;
  240. PSUBSECTION Subsection;
  241. PSUBSECTION LastSubsection;
  242. NTSTATUS Status;
  243. ULONG ConsecutiveFileLockFailures;
  244. ULONG Waited;
  245. LOGICAL EntireRestOfVad;
  246. LOGICAL Attached;
  247. KAPC_STATE ApcState;
  248. PAGED_CODE();
  249. Attached = FALSE;
  250. //
  251. // Determine if the specified base address is within the system
  252. // cache and if so, don't attach, the working set mutex is still
  253. // required to "lock" paged pool pages (proto PTEs) into the
  254. // working set.
  255. //
  256. EndingAddress = (PVOID)(((ULONG_PTR)*BaseAddress + *RegionSize - 1) |
  257. (PAGE_SIZE - 1));
  258. *BaseAddress = PAGE_ALIGN (*BaseAddress);
  259. if (MI_IS_SESSION_ADDRESS (*BaseAddress)) {
  260. //
  261. // Nothing in session space needs flushing.
  262. //
  263. return STATUS_NOT_MAPPED_VIEW;
  264. }
  265. CurrentProcess = PsGetCurrentProcess ();
  266. if (!MI_IS_SYSTEM_CACHE_ADDRESS(*BaseAddress)) {
  267. SystemCache = FALSE;
  268. //
  269. // Attach to the specified process.
  270. //
  271. if (CurrentProcess != Process) {
  272. KeStackAttachProcess (&Process->Pcb, &ApcState);
  273. Attached = TRUE;
  274. }
  275. LOCK_ADDRESS_SPACE (Process);
  276. //
  277. // Make sure the address space was not deleted, if so, return an error.
  278. //
  279. if (Process->Flags & PS_PROCESS_FLAGS_VM_DELETED) {
  280. Status = STATUS_PROCESS_IS_TERMINATING;
  281. goto ErrorReturn;
  282. }
  283. Vad = MiLocateAddress (*BaseAddress);
  284. if (Vad == NULL) {
  285. //
  286. // No Virtual Address Descriptor located for Base Address.
  287. //
  288. Status = STATUS_NOT_MAPPED_VIEW;
  289. goto ErrorReturn;
  290. }
  291. if (*RegionSize == 0) {
  292. EndingAddress = MI_VPN_TO_VA_ENDING (Vad->EndingVpn);
  293. EntireRestOfVad = TRUE;
  294. }
  295. else {
  296. EntireRestOfVad = FALSE;
  297. }
  298. if ((Vad->u.VadFlags.PrivateMemory == 1) ||
  299. (MI_VA_TO_VPN (EndingAddress) > Vad->EndingVpn)) {
  300. //
  301. // This virtual address descriptor does not refer to a Segment
  302. // object.
  303. //
  304. Status = STATUS_NOT_MAPPED_VIEW;
  305. goto ErrorReturn;
  306. }
  307. //
  308. // Make sure this VAD maps a data file (not an image file).
  309. //
  310. ControlArea = Vad->ControlArea;
  311. if ((ControlArea->FilePointer == NULL) ||
  312. (Vad->u.VadFlags.ImageMap == 1)) {
  313. //
  314. // This virtual address descriptor does not refer to a Segment
  315. // object.
  316. //
  317. Status = STATUS_NOT_MAPPED_DATA;
  318. goto ErrorReturn;
  319. }
  320. LOCK_WS_UNSAFE (Process);
  321. }
  322. else {
  323. //
  324. // Initializing Vad, ControlArea and EntireRestOfVad is not needed for
  325. // correctness but without it the compiler cannot compile this code
  326. // W4 to check for use of uninitialized variables.
  327. //
  328. Vad = NULL;
  329. ControlArea = NULL;
  330. EntireRestOfVad = FALSE;
  331. SystemCache = TRUE;
  332. Process = CurrentProcess;
  333. LOCK_WS (Process);
  334. }
  335. PointerPxe = MiGetPxeAddress (*BaseAddress);
  336. PointerPpe = MiGetPpeAddress (*BaseAddress);
  337. PointerPde = MiGetPdeAddress (*BaseAddress);
  338. PointerPte = MiGetPteAddress (*BaseAddress);
  339. LastPte = MiGetPteAddress (EndingAddress);
  340. *RegionSize = (PCHAR)EndingAddress - (PCHAR)*BaseAddress + 1;
  341. retry:
  342. while (!MiDoesPxeExistAndMakeValid (PointerPxe, Process, FALSE, &Waited)) {
  343. //
  344. // This page directory parent entry is empty, go to the next one.
  345. //
  346. PointerPxe += 1;
  347. PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
  348. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  349. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  350. Va = MiGetVirtualAddressMappedByPte (PointerPte);
  351. if (PointerPte > LastPte) {
  352. break;
  353. }
  354. }
  355. while (!MiDoesPpeExistAndMakeValid (PointerPpe, Process, FALSE, &Waited)) {
  356. //
  357. // This page directory parent entry is empty, go to the next one.
  358. //
  359. PointerPpe += 1;
  360. PointerPxe = MiGetPteAddress (PointerPpe);
  361. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  362. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  363. Va = MiGetVirtualAddressMappedByPte (PointerPte);
  364. if (PointerPte > LastPte) {
  365. break;
  366. }
  367. #if (_MI_PAGING_LEVELS >= 4)
  368. if (MiIsPteOnPdeBoundary (PointerPpe)) {
  369. goto retry;
  370. }
  371. #endif
  372. }
  373. Waited = 0;
  374. if (PointerPte <= LastPte) {
  375. while (!MiDoesPdeExistAndMakeValid(PointerPde, Process, FALSE, &Waited)) {
  376. //
  377. // No page table page exists for this address.
  378. //
  379. PointerPde += 1;
  380. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  381. if (PointerPte > LastPte) {
  382. break;
  383. }
  384. #if (_MI_PAGING_LEVELS >= 3)
  385. if (MiIsPteOnPdeBoundary (PointerPde)) {
  386. if (MiIsPteOnPpeBoundary (PointerPde)) {
  387. PointerPxe = MiGetPdeAddress (PointerPde);
  388. }
  389. PointerPpe = MiGetPteAddress (PointerPde);
  390. goto retry;
  391. }
  392. #endif
  393. Va = MiGetVirtualAddressMappedByPte (PointerPte);
  394. }
  395. //
  396. // If the PFN lock (and accordingly the WS mutex) was
  397. // released and reacquired we must retry the operation.
  398. //
  399. if ((PointerPte <= LastPte) && (Waited != 0)) {
  400. goto retry;
  401. }
  402. }
  403. MiFlushDirtyBitsToPfn (PointerPte, LastPte, Process, SystemCache);
  404. if (SystemCache) {
  405. //
  406. // No VADs exist for the system cache.
  407. //
  408. UNLOCK_WS (Process);
  409. Subsection = MiGetSystemCacheSubsection (*BaseAddress, &PointerPte);
  410. LastSubsection = MiGetSystemCacheSubsection (EndingAddress, &FinalPte);
  411. //
  412. // Flush the PTEs from the specified section.
  413. //
  414. Status = MiFlushSectionInternal (PointerPte,
  415. FinalPte,
  416. Subsection,
  417. LastSubsection,
  418. FALSE,
  419. TRUE,
  420. IoStatus);
  421. }
  422. else {
  423. //
  424. // Protect against the section being prematurely deleted.
  425. //
  426. MiFlushAcquire (ControlArea);
  427. PointerPte = MiGetProtoPteAddress (Vad, MI_VA_TO_VPN (*BaseAddress));
  428. Subsection = MiLocateSubsection (Vad, MI_VA_TO_VPN(*BaseAddress));
  429. LastSubsection = MiLocateSubsection (Vad, MI_VA_TO_VPN(EndingAddress));
  430. //
  431. // The last subsection is NULL if the section is not fully
  432. // committed. Only allow the flush if the caller said do the whole
  433. // thing, otherwise it's an error.
  434. //
  435. if (LastSubsection == NULL) {
  436. if (EntireRestOfVad == FALSE) {
  437. //
  438. // Caller can only specify the range that is committed or zero
  439. // to indicate the entire range.
  440. //
  441. UNLOCK_WS_AND_ADDRESS_SPACE (Process);
  442. if (Attached == TRUE) {
  443. KeUnstackDetachProcess (&ApcState);
  444. }
  445. MiFlushRelease (ControlArea);
  446. return STATUS_NOT_MAPPED_VIEW;
  447. }
  448. LastSubsection = Subsection;
  449. while (LastSubsection->NextSubsection) {
  450. LastSubsection = LastSubsection->NextSubsection;
  451. }
  452. //
  453. // A memory barrier is needed to read the subsection chains
  454. // in order to ensure the writes to the actual individual
  455. // subsection data structure fields are visible in correct
  456. // order. This avoids the need to acquire any stronger
  457. // synchronization (ie: PFN lock), thus yielding better
  458. // performance and pagability.
  459. //
  460. KeMemoryBarrier ();
  461. FinalPte = LastSubsection->SubsectionBase + LastSubsection->PtesInSubsection - 1;
  462. }
  463. else {
  464. FinalPte = MiGetProtoPteAddress (Vad, MI_VA_TO_VPN (EndingAddress));
  465. }
  466. UNLOCK_WS_AND_ADDRESS_SPACE (Process);
  467. if (Attached == TRUE) {
  468. KeUnstackDetachProcess (&ApcState);
  469. }
  470. //
  471. // Preacquire the file to synchronize the flush.
  472. //
  473. ConsecutiveFileLockFailures = 0;
  474. do {
  475. Status = FsRtlAcquireFileForCcFlushEx (ControlArea->FilePointer);
  476. if (!NT_SUCCESS(Status)) {
  477. break;
  478. }
  479. //
  480. // Flush the PTEs from the specified section.
  481. //
  482. Status = MiFlushSectionInternal (PointerPte,
  483. FinalPte,
  484. Subsection,
  485. LastSubsection,
  486. TRUE,
  487. TRUE,
  488. IoStatus);
  489. //
  490. // Release the file we acquired.
  491. //
  492. FsRtlReleaseFileForCcFlush (ControlArea->FilePointer);
  493. //
  494. // Only try the request more than once if the filesystem told us
  495. // it had a deadlock.
  496. //
  497. if (Status != STATUS_FILE_LOCK_CONFLICT) {
  498. break;
  499. }
  500. ConsecutiveFileLockFailures += 1;
  501. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  502. } while (ConsecutiveFileLockFailures < 5);
  503. MiFlushRelease (ControlArea);
  504. }
  505. return Status;
  506. ErrorReturn:
  507. ASSERT (SystemCache == FALSE);
  508. UNLOCK_ADDRESS_SPACE (Process);
  509. if (Attached == TRUE) {
  510. KeUnstackDetachProcess (&ApcState);
  511. }
  512. return Status;
  513. }
  514. NTSTATUS
  515. MmFlushSection (
  516. IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
  517. IN PLARGE_INTEGER Offset,
  518. IN SIZE_T RegionSize,
  519. OUT PIO_STATUS_BLOCK IoStatus,
  520. IN ULONG AcquireFile
  521. )
  522. /*++
  523. Routine Description:
  524. This function flushes to the backing file any modified pages within
  525. the specified range of the section.
  526. Arguments:
  527. SectionObjectPointer - Supplies a pointer to the section objects.
  528. Offset - Supplies the offset into the section in which to begin
  529. flushing pages. If this argument is not present, then the
  530. whole section is flushed without regard to the region size
  531. argument.
  532. RegionSize - Supplies the size in bytes to flush. This is rounded
  533. to a page multiple.
  534. IoStatus - Returns the value of the IoStatus for the last attempted
  535. I/O operation.
  536. AcquireFile - Nonzero if the callback should be used to acquire the file.
  537. Return Value:
  538. Returns status of the operation.
  539. --*/
  540. {
  541. PCONTROL_AREA ControlArea;
  542. PMMPTE PointerPte;
  543. PMMPTE LastPte;
  544. KIRQL OldIrql;
  545. ULONG PteOffset;
  546. ULONG LastPteOffset;
  547. PSUBSECTION Subsection;
  548. PSUBSECTION TempSubsection;
  549. PSUBSECTION LastSubsection;
  550. PSUBSECTION LastSubsectionWithProtos;
  551. PMAPPED_FILE_SEGMENT Segment;
  552. PETHREAD CurrentThread;
  553. NTSTATUS status;
  554. BOOLEAN OldClusterState;
  555. ULONG ConsecutiveFileLockFailures;
  556. //
  557. // Initialize IoStatus for success, in case we take an early exit.
  558. //
  559. IoStatus->Status = STATUS_SUCCESS;
  560. IoStatus->Information = RegionSize;
  561. LOCK_PFN (OldIrql);
  562. ControlArea = ((PCONTROL_AREA)(SectionObjectPointer->DataSectionObject));
  563. ASSERT ((ControlArea == NULL) || (ControlArea->u.Flags.Image == 0));
  564. if ((ControlArea == NULL) ||
  565. (ControlArea->u.Flags.BeingDeleted) ||
  566. (ControlArea->u.Flags.BeingCreated) ||
  567. (ControlArea->u.Flags.Rom) ||
  568. (ControlArea->NumberOfPfnReferences == 0)) {
  569. //
  570. // This file no longer has an associated segment or is in the
  571. // process of coming or going.
  572. // If the number of PFN references is zero, then this control
  573. // area does not have any valid or transition pages that need
  574. // to be flushed.
  575. //
  576. UNLOCK_PFN (OldIrql);
  577. return STATUS_SUCCESS;
  578. }
  579. //
  580. // Locate the subsection.
  581. //
  582. ASSERT (ControlArea->u.Flags.Image == 0);
  583. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  584. ASSERT (ControlArea->u.Flags.PhysicalMemory == 0);
  585. Subsection = (PSUBSECTION)(ControlArea + 1);
  586. if (!ARGUMENT_PRESENT (Offset)) {
  587. //
  588. // If the offset is not specified, flush the complete file ignoring
  589. // the region size.
  590. //
  591. ASSERT (ControlArea->FilePointer != NULL);
  592. PteOffset = 0;
  593. LastSubsection = Subsection;
  594. Segment = (PMAPPED_FILE_SEGMENT) ControlArea->Segment;
  595. if (MmIsAddressValid (Segment)) {
  596. if (Segment->LastSubsectionHint != NULL) {
  597. LastSubsection = (PSUBSECTION) Segment->LastSubsectionHint;
  598. }
  599. }
  600. while (LastSubsection->NextSubsection != NULL) {
  601. LastSubsection = LastSubsection->NextSubsection;
  602. }
  603. LastPteOffset = LastSubsection->PtesInSubsection - 1;
  604. }
  605. else {
  606. PteOffset = (ULONG)(Offset->QuadPart >> PAGE_SHIFT);
  607. //
  608. // Make sure the PTEs are not in the extended part of the segment.
  609. //
  610. while (PteOffset >= Subsection->PtesInSubsection) {
  611. PteOffset -= Subsection->PtesInSubsection;
  612. if (Subsection->NextSubsection == NULL) {
  613. //
  614. // Past end of mapping, just return success.
  615. //
  616. UNLOCK_PFN (OldIrql);
  617. return STATUS_SUCCESS;
  618. }
  619. Subsection = Subsection->NextSubsection;
  620. }
  621. ASSERT (PteOffset < Subsection->PtesInSubsection);
  622. //
  623. // Locate the address of the last prototype PTE to be flushed.
  624. //
  625. LastPteOffset = PteOffset + (ULONG)(((RegionSize + BYTE_OFFSET(Offset->LowPart)) - 1) >> PAGE_SHIFT);
  626. LastSubsection = Subsection;
  627. while (LastPteOffset >= LastSubsection->PtesInSubsection) {
  628. LastPteOffset -= LastSubsection->PtesInSubsection;
  629. if (LastSubsection->NextSubsection == NULL) {
  630. LastPteOffset = LastSubsection->PtesInSubsection - 1;
  631. break;
  632. }
  633. LastSubsection = LastSubsection->NextSubsection;
  634. }
  635. ASSERT (LastPteOffset < LastSubsection->PtesInSubsection);
  636. }
  637. //
  638. // Try for the fast reference on the first and last subsection.
  639. // If that cannot be gotten, then there are no prototype PTEs for this
  640. // subsection, therefore there is nothing in it to flush so leap forwards.
  641. //
  642. // Note that subsections in between do not need referencing as
  643. // MiFlushSectionInternal is smart enough to skip them if they're
  644. // nonresident.
  645. //
  646. if (MiReferenceSubsection ((PMSUBSECTION)Subsection) == FALSE) {
  647. do {
  648. //
  649. // If this increment would put us past the end offset, then nothing
  650. // to flush, just return success.
  651. //
  652. if (Subsection == LastSubsection) {
  653. UNLOCK_PFN (OldIrql);
  654. return STATUS_SUCCESS;
  655. }
  656. Subsection = Subsection->NextSubsection;
  657. //
  658. // If this increment put us past the end of section, then nothing
  659. // to flush, just return success.
  660. //
  661. if (Subsection == NULL) {
  662. UNLOCK_PFN (OldIrql);
  663. return STATUS_SUCCESS;
  664. }
  665. if ((PMSUBSECTION)Subsection->SubsectionBase == NULL) {
  666. continue;
  667. }
  668. if (MiReferenceSubsection ((PMSUBSECTION)Subsection) == FALSE) {
  669. continue;
  670. }
  671. //
  672. // Start the flush at this subsection which is now referenced.
  673. //
  674. PointerPte = &Subsection->SubsectionBase[0];
  675. break;
  676. } while (TRUE);
  677. }
  678. else {
  679. PointerPte = &Subsection->SubsectionBase[PteOffset];
  680. }
  681. ASSERT (Subsection->SubsectionBase != NULL);
  682. //
  683. // The first subsection is referenced, now reference count the last one.
  684. // If the first is the last, just double reference it anyway as it
  685. // simplifies cleanup later.
  686. //
  687. if (MiReferenceSubsection ((PMSUBSECTION)LastSubsection) == FALSE) {
  688. ASSERT (Subsection != LastSubsection);
  689. TempSubsection = Subsection->NextSubsection;
  690. LastSubsectionWithProtos = NULL;
  691. while (TempSubsection != LastSubsection) {
  692. //
  693. // If this increment put us past the end of section, then nothing
  694. // to flush, just return success.
  695. //
  696. ASSERT (TempSubsection != NULL);
  697. if ((PMSUBSECTION)TempSubsection->SubsectionBase != NULL) {
  698. LastSubsectionWithProtos = TempSubsection;
  699. }
  700. TempSubsection = TempSubsection->NextSubsection;
  701. }
  702. //
  703. // End the flush at this subsection and reference it.
  704. //
  705. if (LastSubsectionWithProtos == NULL) {
  706. ASSERT (Subsection != NULL);
  707. ASSERT (Subsection->SubsectionBase != NULL);
  708. TempSubsection = Subsection;
  709. }
  710. else {
  711. TempSubsection = LastSubsectionWithProtos;
  712. }
  713. if (MiReferenceSubsection ((PMSUBSECTION)TempSubsection) == FALSE) {
  714. ASSERT (FALSE);
  715. }
  716. ASSERT (TempSubsection->SubsectionBase != NULL);
  717. LastSubsection = TempSubsection;
  718. LastPteOffset = LastSubsection->PtesInSubsection - 1;
  719. }
  720. //
  721. // End the flush at this subsection which is now referenced.
  722. //
  723. LastPte = &LastSubsection->SubsectionBase[LastPteOffset];
  724. //
  725. // Up the map view count so the control area cannot be deleted
  726. // out from under the call.
  727. //
  728. ControlArea->NumberOfMappedViews += 1;
  729. UNLOCK_PFN (OldIrql);
  730. CurrentThread = PsGetCurrentThread();
  731. //
  732. // Indicate that disk verify errors should be returned as exceptions.
  733. //
  734. OldClusterState = CurrentThread->ForwardClusterOnly;
  735. CurrentThread->ForwardClusterOnly = TRUE;
  736. //
  737. // Preacquire the file if we are going to synchronize the flush.
  738. //
  739. if (AcquireFile == 0) {
  740. //
  741. // Flush the PTEs from the specified section.
  742. //
  743. status = MiFlushSectionInternal (PointerPte,
  744. LastPte,
  745. Subsection,
  746. LastSubsection,
  747. TRUE,
  748. TRUE,
  749. IoStatus);
  750. }
  751. else {
  752. ConsecutiveFileLockFailures = 0;
  753. do {
  754. status = FsRtlAcquireFileForCcFlushEx (ControlArea->FilePointer);
  755. if (!NT_SUCCESS(status)) {
  756. break;
  757. }
  758. //
  759. // Flush the PTEs from the specified section.
  760. //
  761. status = MiFlushSectionInternal (PointerPte,
  762. LastPte,
  763. Subsection,
  764. LastSubsection,
  765. TRUE,
  766. TRUE,
  767. IoStatus);
  768. //
  769. // Release the file we acquired.
  770. //
  771. FsRtlReleaseFileForCcFlush (ControlArea->FilePointer);
  772. //
  773. // Only try the request more than once if the filesystem told us
  774. // it had a deadlock.
  775. //
  776. if (status != STATUS_FILE_LOCK_CONFLICT) {
  777. break;
  778. }
  779. ConsecutiveFileLockFailures += 1;
  780. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  781. } while (ConsecutiveFileLockFailures < 5);
  782. }
  783. CurrentThread->ForwardClusterOnly = OldClusterState;
  784. LOCK_PFN (OldIrql);
  785. MiDecrementSubsections (Subsection, Subsection);
  786. MiDecrementSubsections (LastSubsection, LastSubsection);
  787. ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1);
  788. ControlArea->NumberOfMappedViews -= 1;
  789. //
  790. // Check to see if the control area should be deleted. This
  791. // will release the PFN lock.
  792. //
  793. MiCheckControlArea (ControlArea, NULL, OldIrql);
  794. return status;
  795. }
  796. LONGLONG
  797. MiStartingOffset(
  798. IN PSUBSECTION Subsection,
  799. IN PMMPTE PteAddress
  800. )
  801. /*++
  802. Routine Description:
  803. This function calculates the file offset given a subsection and a PTE
  804. offset. Note that images are stored in 512-byte units whereas data is
  805. stored in 4K units.
  806. When this is all debugged, this should be made into a macro.
  807. Arguments:
  808. Subsection - Supplies a subsection to reference for the file address.
  809. PteAddress - Supplies a PTE within the subsection
  810. Return Value:
  811. Returns the file offset to obtain the backing data from.
  812. --*/
  813. {
  814. LONGLONG PteByteOffset;
  815. LARGE_INTEGER StartAddress;
  816. if (Subsection->ControlArea->u.Flags.Image == 1) {
  817. return MI_STARTING_OFFSET ( Subsection,
  818. PteAddress);
  819. }
  820. ASSERT (Subsection->SubsectionBase != NULL);
  821. PteByteOffset = (LONGLONG)((PteAddress - Subsection->SubsectionBase))
  822. << PAGE_SHIFT;
  823. Mi4KStartFromSubsection (&StartAddress, Subsection);
  824. StartAddress.QuadPart = StartAddress.QuadPart << MM4K_SHIFT;
  825. PteByteOffset += StartAddress.QuadPart;
  826. return PteByteOffset;
  827. }
  828. LARGE_INTEGER
  829. MiEndingOffset(
  830. IN PSUBSECTION Subsection
  831. )
  832. /*++
  833. Routine Description:
  834. This function calculates the last valid file offset in a given subsection.
  835. offset. Note that images are stored in 512-byte units whereas data is
  836. stored in 4K units.
  837. When this is all debugged, this should be made into a macro.
  838. Arguments:
  839. Subsection - Supplies a subsection to reference for the file address.
  840. PteAddress - Supplies a PTE within the subsection
  841. Return Value:
  842. Returns the file offset to obtain the backing data from.
  843. --*/
  844. {
  845. LARGE_INTEGER FileByteOffset;
  846. if (Subsection->ControlArea->u.Flags.Image == 1) {
  847. FileByteOffset.QuadPart =
  848. (Subsection->StartingSector + Subsection->NumberOfFullSectors) <<
  849. MMSECTOR_SHIFT;
  850. }
  851. else {
  852. Mi4KStartFromSubsection (&FileByteOffset, Subsection);
  853. FileByteOffset.QuadPart += Subsection->NumberOfFullSectors;
  854. FileByteOffset.QuadPart = FileByteOffset.QuadPart << MM4K_SHIFT;
  855. }
  856. FileByteOffset.QuadPart += Subsection->u.SubsectionFlags.SectorEndOffset;
  857. return FileByteOffset;
  858. }
  859. NTSTATUS
  860. MiFlushSectionInternal (
  861. IN PMMPTE StartingPte,
  862. IN PMMPTE FinalPte,
  863. IN PSUBSECTION FirstSubsection,
  864. IN PSUBSECTION LastSubsection,
  865. IN ULONG Synchronize,
  866. IN LOGICAL WriteInProgressOk,
  867. OUT PIO_STATUS_BLOCK IoStatus
  868. )
  869. /*++
  870. Routine Description:
  871. This function flushes to the backing file any modified pages within
  872. the specified range of the section. The parameters describe the
  873. section's prototype PTEs (start and end) and the subsections
  874. which correspond to the starting and ending PTE.
  875. Each PTE in the subsection between the specified start and end
  876. is examined and if the page is either valid or transition AND
  877. the page has been modified, the modify bit is cleared in the PFN
  878. database and the page is flushed to its backing file.
  879. Arguments:
  880. StartingPte - Supplies a pointer to the first prototype PTE to
  881. be examined for flushing.
  882. FinalPte - Supplies a pointer to the last prototype PTE to be
  883. examined for flushing.
  884. FirstSubsection - Supplies the subsection that contains the
  885. StartingPte.
  886. LastSubsection - Supplies the subsection that contains the
  887. FinalPte.
  888. Synchronize - Supplies TRUE if synchronization with all threads
  889. doing flush operations to this section should occur.
  890. WriteInProgressOk - Supplies TRUE if the caller can tolerate a write
  891. already in progress for any dirty pages.
  892. IoStatus - Returns the value of the IoStatus for the last attempted
  893. I/O operation.
  894. Return Value:
  895. Returns status of the operation.
  896. --*/
  897. {
  898. LOGICAL DroppedPfnLock;
  899. PCONTROL_AREA ControlArea;
  900. PMMPTE PointerPte;
  901. PMMPTE LastPte;
  902. PMMPTE LastWritten;
  903. PMMPTE FirstWritten;
  904. MMPTE PteContents;
  905. PMMPFN Pfn1;
  906. PMMPFN Pfn2;
  907. KIRQL OldIrql;
  908. PMDL Mdl;
  909. KEVENT IoEvent;
  910. PSUBSECTION Subsection;
  911. PMSUBSECTION MappedSubsection;
  912. PPFN_NUMBER Page;
  913. PFN_NUMBER PageFrameIndex;
  914. PPFN_NUMBER LastPage;
  915. NTSTATUS Status;
  916. UINT64 StartingOffset;
  917. UINT64 TempOffset;
  918. LOGICAL WriteNow;
  919. LOGICAL Bail;
  920. PFN_NUMBER MdlHack[(sizeof(MDL)/sizeof(PFN_NUMBER)) + (MM_MAXIMUM_DISK_IO_SIZE / PAGE_SIZE) + 1];
  921. ULONG ReflushCount;
  922. ULONG MaxClusterSize;
  923. PFILE_OBJECT FilePointer;
  924. LOGICAL CurrentThreadIsDereferenceThread;
  925. //
  926. // WriteInProgressOk is only FALSE when the segment dereference thread is
  927. // doing a top-level flush just prior to cleaning the section or subsection.
  928. // Note that this flag may be TRUE even for the dereference thread because
  929. // the dereference thread calls filesystems who may then issue a flush.
  930. //
  931. if (WriteInProgressOk == FALSE) {
  932. CurrentThreadIsDereferenceThread = TRUE;
  933. ASSERT (PsGetCurrentThread()->StartAddress == (PVOID)(ULONG_PTR)MiDereferenceSegmentThread);
  934. }
  935. else {
  936. CurrentThreadIsDereferenceThread = FALSE;
  937. //
  938. // This may actually be the dereference thread as the segment deletion
  939. // dereferences the file object potentially calling the filesystem which
  940. // may then issue a CcFlushCache/MmFlushSection. For our purposes,
  941. // lower level flushes in this context are treated as though they
  942. // came from a different thread.
  943. //
  944. }
  945. WriteNow = FALSE;
  946. Bail = FALSE;
  947. IoStatus->Status = STATUS_SUCCESS;
  948. IoStatus->Information = 0;
  949. Mdl = (PMDL)&MdlHack[0];
  950. KeInitializeEvent (&IoEvent, NotificationEvent, FALSE);
  951. FinalPte += 1; // Point to 1 past the last one.
  952. FirstWritten = NULL;
  953. LastWritten = NULL;
  954. LastPage = 0;
  955. Subsection = FirstSubsection;
  956. PointerPte = StartingPte;
  957. ControlArea = FirstSubsection->ControlArea;
  958. FilePointer = ControlArea->FilePointer;
  959. ASSERT ((ControlArea->u.Flags.Image == 0) &&
  960. (FilePointer != NULL) &&
  961. (ControlArea->u.Flags.PhysicalMemory == 0));
  962. //
  963. // Initializing these is not needed for correctness
  964. // but without it the compiler cannot compile this code
  965. // W4 to check for use of uninitialized variables.
  966. //
  967. MappedSubsection = NULL;
  968. StartingOffset = 0;
  969. //
  970. // Try to cluster pages as long as the storage stack can handle it.
  971. //
  972. MaxClusterSize = MmModifiedWriteClusterSize;
  973. LOCK_PFN (OldIrql);
  974. ASSERT (ControlArea->u.Flags.Image == 0);
  975. if (ControlArea->NumberOfPfnReferences == 0) {
  976. //
  977. // No transition or valid prototype PTEs present, hence
  978. // no need to flush anything.
  979. //
  980. UNLOCK_PFN (OldIrql);
  981. return STATUS_SUCCESS;
  982. }
  983. while ((Synchronize) && (ControlArea->FlushInProgressCount != 0)) {
  984. //
  985. // Another thread is currently performing a flush operation on
  986. // this file. Wait for that flush to complete.
  987. //
  988. ControlArea->u.Flags.CollidedFlush = 1;
  989. //
  990. // Keep APCs blocked so no special APCs can be delivered in KeWait
  991. // which would cause the dispatcher lock to be released opening a
  992. // window where this thread could miss a pulse.
  993. //
  994. UNLOCK_PFN_AND_THEN_WAIT (APC_LEVEL);
  995. KeWaitForSingleObject (&MmCollidedFlushEvent,
  996. WrPageOut,
  997. KernelMode,
  998. FALSE,
  999. (PLARGE_INTEGER)&MmOneSecond);
  1000. KeLowerIrql (OldIrql);
  1001. LOCK_PFN (OldIrql);
  1002. }
  1003. ControlArea->FlushInProgressCount += 1;
  1004. //
  1005. // Clear the deferred entry list as pages from it may get marked modified
  1006. // during the processing. Note that any transition page which is currently
  1007. // clean but has a nonzero reference count may get marked modified if
  1008. // there is a pending transaction and note well that this transaction may
  1009. // complete at any time ! Thus, this case must be carefully handled.
  1010. //
  1011. #if !defined(MI_MULTINODE)
  1012. if (MmPfnDeferredList != NULL) {
  1013. MiDeferredUnlockPages (MI_DEFER_PFN_HELD);
  1014. }
  1015. #else
  1016. //
  1017. // Each and every node's deferred list would have to be checked so
  1018. // we might as well go the long way and just call.
  1019. //
  1020. MiDeferredUnlockPages (MI_DEFER_PFN_HELD);
  1021. #endif
  1022. for (;;) {
  1023. if (LastSubsection != Subsection) {
  1024. //
  1025. // Flush to the last PTE in this subsection.
  1026. //
  1027. LastPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
  1028. }
  1029. else {
  1030. //
  1031. // Flush to the end of the range.
  1032. //
  1033. LastPte = FinalPte;
  1034. }
  1035. if (Subsection->SubsectionBase == NULL) {
  1036. //
  1037. // The prototype PTEs for this subsection have either never been
  1038. // created or have been tossed due to memory pressure. Either
  1039. // way, this range can be skipped as there are obviously no
  1040. // dirty pages in it. If there are other dirty pages
  1041. // to be written, write them now as we are skipping over PTEs.
  1042. //
  1043. if (LastWritten != NULL) {
  1044. ASSERT (MappedSubsection != NULL);
  1045. WriteNow = TRUE;
  1046. goto CheckForWrite;
  1047. }
  1048. if (LastSubsection == Subsection) {
  1049. break;
  1050. }
  1051. Subsection = Subsection->NextSubsection;
  1052. PointerPte = Subsection->SubsectionBase;
  1053. continue;
  1054. }
  1055. //
  1056. // Up the number of mapped views to prevent other threads
  1057. // from freeing this to the unused subsection list while we're
  1058. // operating on it.
  1059. //
  1060. MappedSubsection = (PMSUBSECTION) Subsection;
  1061. MappedSubsection->NumberOfMappedViews += 1;
  1062. if (MappedSubsection->DereferenceList.Flink != NULL) {
  1063. //
  1064. // Remove this from the list of unused subsections.
  1065. //
  1066. RemoveEntryList (&MappedSubsection->DereferenceList);
  1067. MI_UNUSED_SUBSECTIONS_COUNT_REMOVE (MappedSubsection);
  1068. MappedSubsection->DereferenceList.Flink = NULL;
  1069. }
  1070. if (CurrentThreadIsDereferenceThread == FALSE) {
  1071. //
  1072. // Set the access bit so an already ongoing trim won't blindly
  1073. // delete the prototype PTEs on completion of a mapped write.
  1074. // This can happen if the current thread dirties some pages and
  1075. // then deletes the view before the trim write finishes - this
  1076. // bit informs the trimming thread that a rescan is needed so
  1077. // that writes are not lost.
  1078. //
  1079. MappedSubsection->u2.SubsectionFlags2.SubsectionAccessed = 1;
  1080. }
  1081. //
  1082. // If the prototype PTEs are paged out or have a share count
  1083. // of 1, they cannot contain any transition or valid PTEs.
  1084. //
  1085. if (!MiCheckProtoPtePageState(PointerPte, TRUE, &DroppedPfnLock)) {
  1086. PointerPte = (PMMPTE)(((ULONG_PTR)PointerPte | (PAGE_SIZE - 1)) + 1);
  1087. }
  1088. while (PointerPte < LastPte) {
  1089. if (MiIsPteOnPdeBoundary(PointerPte)) {
  1090. //
  1091. // We are on a page boundary, make sure this PTE is resident.
  1092. //
  1093. if (!MiCheckProtoPtePageState(PointerPte, TRUE, &DroppedPfnLock)) {
  1094. PointerPte = (PMMPTE)((PCHAR)PointerPte + PAGE_SIZE);
  1095. //
  1096. // If there are dirty pages to be written, write them
  1097. // now as we are skipping over PTEs.
  1098. //
  1099. if (LastWritten != NULL) {
  1100. WriteNow = TRUE;
  1101. goto CheckForWrite;
  1102. }
  1103. continue;
  1104. }
  1105. }
  1106. PteContents = *PointerPte;
  1107. if ((PteContents.u.Hard.Valid == 1) ||
  1108. ((PteContents.u.Soft.Prototype == 0) &&
  1109. (PteContents.u.Soft.Transition == 1))) {
  1110. //
  1111. // Prototype PTE in transition, there are 3 possible cases:
  1112. // 1. The page is part of an image which is sharable and
  1113. // refers to the paging file - dereference page file
  1114. // space and free the physical page.
  1115. // 2. The page refers to the segment but is not modified -
  1116. // free the physical page.
  1117. // 3. The page refers to the segment and is modified -
  1118. // write the page to the file and free the physical page.
  1119. //
  1120. if (PteContents.u.Hard.Valid == 1) {
  1121. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
  1122. }
  1123. else {
  1124. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
  1125. }
  1126. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1127. ASSERT (Pfn1->OriginalPte.u.Soft.Prototype == 1);
  1128. ASSERT (Pfn1->OriginalPte.u.Hard.Valid == 0);
  1129. //
  1130. // Note that any transition page which is currently clean but
  1131. // has a nonzero reference count may get marked modified if
  1132. // there is a pending transaction and note well that this
  1133. // transaction may complete at any time ! Thus, this case
  1134. // must be carefully handled since the segment dereference
  1135. // thread must be given a collision error for this one as it
  1136. // requires that no pages be dirtied after a successful return.
  1137. //
  1138. if ((CurrentThreadIsDereferenceThread == TRUE) &&
  1139. (Pfn1->u3.e2.ReferenceCount != 0)) {
  1140. #if DBG
  1141. if ((PteContents.u.Hard.Valid != 0) &&
  1142. (MappedSubsection->u2.SubsectionFlags2.SubsectionAccessed == 0) &&
  1143. (ControlArea->u.Flags.Accessed == 0)) {
  1144. if (!KdDebuggerNotPresent) {
  1145. DbgPrint ("MM: flushing valid proto, %p %p\n",
  1146. Pfn1, PointerPte);
  1147. DbgBreakPoint ();
  1148. }
  1149. }
  1150. #endif
  1151. PointerPte = LastPte;
  1152. Bail = TRUE;
  1153. if (LastWritten != NULL) {
  1154. WriteNow = TRUE;
  1155. }
  1156. goto CheckForWrite;
  1157. }
  1158. //
  1159. // If the page is modified OR a write is in progress
  1160. // flush it. The write in progress case catches problems
  1161. // where the modified page write continually writes a
  1162. // page and gets errors writing it, by writing pages
  1163. // in this state, the error will be propagated back to
  1164. // the caller.
  1165. //
  1166. if ((Pfn1->u3.e1.Modified == 1) ||
  1167. (Pfn1->u3.e1.WriteInProgress)) {
  1168. if ((WriteInProgressOk == FALSE) &&
  1169. (Pfn1->u3.e1.WriteInProgress)) {
  1170. PointerPte = LastPte;
  1171. Bail = TRUE;
  1172. if (LastWritten != NULL) {
  1173. WriteNow = TRUE;
  1174. }
  1175. goto CheckForWrite;
  1176. }
  1177. if (LastWritten == NULL) {
  1178. //
  1179. // This is the first page of a cluster, initialize
  1180. // the MDL, etc.
  1181. //
  1182. LastPage = (PPFN_NUMBER)(Mdl + 1);
  1183. //
  1184. // Calculate the offset to read into the file.
  1185. // offset = base + ((thispte - basepte) << PAGE_SHIFT)
  1186. //
  1187. StartingOffset = (UINT64) MiStartingOffset (
  1188. Subsection,
  1189. Pfn1->PteAddress);
  1190. MI_INITIALIZE_ZERO_MDL (Mdl);
  1191. Mdl->MdlFlags |= MDL_PAGES_LOCKED;
  1192. Mdl->StartVa =
  1193. (PVOID)ULongToPtr(Pfn1->u3.e1.PageColor << PAGE_SHIFT);
  1194. Mdl->Size = (CSHORT)(sizeof(MDL) +
  1195. (sizeof(PFN_NUMBER) * MaxClusterSize));
  1196. FirstWritten = PointerPte;
  1197. }
  1198. LastWritten = PointerPte;
  1199. Mdl->ByteCount += PAGE_SIZE;
  1200. if (Mdl->ByteCount == (PAGE_SIZE * MaxClusterSize)) {
  1201. WriteNow = TRUE;
  1202. }
  1203. if (PteContents.u.Hard.Valid == 0) {
  1204. //
  1205. // The page is in transition.
  1206. //
  1207. MiUnlinkPageFromList (Pfn1);
  1208. MI_ADD_LOCKED_PAGE_CHARGE_FOR_MODIFIED_PAGE(Pfn1, 18);
  1209. }
  1210. else {
  1211. MI_ADD_LOCKED_PAGE_CHARGE(Pfn1, 20);
  1212. }
  1213. //
  1214. // Clear the modified bit for this page.
  1215. //
  1216. MI_SET_MODIFIED (Pfn1, 0, 0x22);
  1217. //
  1218. // Up the reference count for the physical page as there
  1219. // is I/O in progress.
  1220. //
  1221. Pfn1->u3.e2.ReferenceCount += 1;
  1222. *LastPage = PageFrameIndex;
  1223. LastPage += 1;
  1224. }
  1225. else {
  1226. //
  1227. // This page was not modified and therefore ends the
  1228. // current write cluster if any. Set WriteNow to TRUE
  1229. // if there is a cluster being built.
  1230. //
  1231. if (LastWritten != NULL) {
  1232. WriteNow = TRUE;
  1233. }
  1234. }
  1235. }
  1236. else {
  1237. //
  1238. // This page was not modified and therefore ends the
  1239. // current write cluster if any. Set WriteNow to TRUE
  1240. // if there is a cluster being built.
  1241. //
  1242. if (LastWritten != NULL) {
  1243. WriteNow = TRUE;
  1244. }
  1245. }
  1246. PointerPte += 1;
  1247. CheckForWrite:
  1248. //
  1249. // Write the current cluster if it is complete,
  1250. // full, or the loop is now complete.
  1251. //
  1252. if ((WriteNow) ||
  1253. ((PointerPte == LastPte) && (LastWritten != NULL))) {
  1254. LARGE_INTEGER EndOfFile;
  1255. //
  1256. // Issue the write request.
  1257. //
  1258. UNLOCK_PFN (OldIrql);
  1259. WriteNow = FALSE;
  1260. //
  1261. // Make sure the write does not go past the
  1262. // end of file. (segment size).
  1263. //
  1264. EndOfFile = MiEndingOffset(Subsection);
  1265. TempOffset = (UINT64) EndOfFile.QuadPart;
  1266. if (StartingOffset + Mdl->ByteCount > TempOffset) {
  1267. ASSERT ((ULONG_PTR)(TempOffset - StartingOffset) >
  1268. (Mdl->ByteCount - PAGE_SIZE));
  1269. Mdl->ByteCount = (ULONG)(TempOffset- StartingOffset);
  1270. }
  1271. ReflushCount = 0;
  1272. while (TRUE) {
  1273. KeClearEvent (&IoEvent);
  1274. Status = IoSynchronousPageWrite (FilePointer,
  1275. Mdl,
  1276. (PLARGE_INTEGER)&StartingOffset,
  1277. &IoEvent,
  1278. IoStatus);
  1279. if (NT_SUCCESS(Status)) {
  1280. //
  1281. // Success was returned, so wait for the i/o event.
  1282. //
  1283. KeWaitForSingleObject (&IoEvent,
  1284. WrPageOut,
  1285. KernelMode,
  1286. FALSE,
  1287. NULL);
  1288. }
  1289. else {
  1290. //
  1291. // Copy the error to the IoStatus, for error
  1292. // handling below.
  1293. //
  1294. IoStatus->Status = Status;
  1295. }
  1296. if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) {
  1297. MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
  1298. }
  1299. if (MmIsRetryIoStatus(IoStatus->Status)) {
  1300. ReflushCount -= 1;
  1301. if (ReflushCount & MiIoRetryMask) {
  1302. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&Mm30Milliseconds);
  1303. continue;
  1304. }
  1305. }
  1306. break;
  1307. }
  1308. Page = (PPFN_NUMBER)(Mdl + 1);
  1309. LOCK_PFN (OldIrql);
  1310. if (MiIsPteOnPdeBoundary(PointerPte) == 0) {
  1311. //
  1312. // The next PTE is not in a different page, make
  1313. // sure the PTE for the prototype PTE page was not
  1314. // put in transition while the I/O was in progress.
  1315. // Note the prototype PTE page itself cannot be reused
  1316. // as each outstanding page has a sharecount on it - but
  1317. // the PTE mapping it can be put in transition regardless
  1318. // of sharecount because it is a system page.
  1319. //
  1320. MiMakeSystemAddressValidPfn (PointerPte);
  1321. }
  1322. if (NT_SUCCESS(IoStatus->Status)) {
  1323. //
  1324. // The I/O completed successfully, unlock the pages.
  1325. //
  1326. while (Page < LastPage) {
  1327. Pfn2 = MI_PFN_ELEMENT (*Page);
  1328. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(Pfn2, 19);
  1329. Page += 1;
  1330. }
  1331. }
  1332. else {
  1333. //
  1334. // Don't count on the file system to convey
  1335. // anything in the information field on errors.
  1336. //
  1337. IoStatus->Information = 0;
  1338. //
  1339. // The I/O completed unsuccessfully, unlock the pages
  1340. // and return an error status.
  1341. //
  1342. while (Page < LastPage) {
  1343. Pfn2 = MI_PFN_ELEMENT (*Page);
  1344. //
  1345. // Mark the page dirty again so it can be rewritten.
  1346. //
  1347. MI_SET_MODIFIED (Pfn2, 1, 0x1);
  1348. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF (Pfn2, 21);
  1349. Page += 1;
  1350. }
  1351. if ((MmIsRetryIoStatus(IoStatus->Status)) &&
  1352. (MaxClusterSize != 1) &&
  1353. (Mdl->ByteCount > PAGE_SIZE)) {
  1354. //
  1355. // Retries of a cluster have failed, reissue
  1356. // the cluster one page at a time as the
  1357. // storage stack should always be able to
  1358. // make forward progress this way.
  1359. //
  1360. ASSERT (FirstWritten != NULL);
  1361. ASSERT (LastWritten != NULL);
  1362. ASSERT (FirstWritten != LastWritten);
  1363. PointerPte = FirstWritten;
  1364. MiMakeSystemAddressValidPfn (PointerPte);
  1365. MaxClusterSize = 1;
  1366. }
  1367. else {
  1368. //
  1369. // Calculate how much was written thus far
  1370. // and add that to the information field
  1371. // of the IOSB.
  1372. //
  1373. IoStatus->Information +=
  1374. (((LastWritten - StartingPte) << PAGE_SHIFT) -
  1375. Mdl->ByteCount);
  1376. LastWritten = NULL;
  1377. //
  1378. // Set this to force termination of the outermost loop.
  1379. //
  1380. Subsection = LastSubsection;
  1381. break;
  1382. }
  1383. } // end if error on i/o
  1384. //
  1385. // As the PFN lock has been released and
  1386. // reacquired, do this loop again as the
  1387. // PTE may have changed state.
  1388. //
  1389. LastWritten = NULL;
  1390. } // end if chunk to write
  1391. } //end while
  1392. ASSERT (MappedSubsection->DereferenceList.Flink == NULL);
  1393. ASSERT ((MappedSubsection->NumberOfMappedViews >= 1) ||
  1394. (MappedSubsection->u.SubsectionFlags.SubsectionStatic == 1));
  1395. MappedSubsection->NumberOfMappedViews -= 1;
  1396. if ((MappedSubsection->NumberOfMappedViews == 0) &&
  1397. (MappedSubsection->u.SubsectionFlags.SubsectionStatic == 0)) {
  1398. //
  1399. // Insert this subsection into the unused subsection list.
  1400. //
  1401. InsertTailList (&MmUnusedSubsectionList,
  1402. &MappedSubsection->DereferenceList);
  1403. MI_UNUSED_SUBSECTIONS_COUNT_INSERT (MappedSubsection);
  1404. }
  1405. if ((Bail == TRUE) || (Subsection == LastSubsection)) {
  1406. //
  1407. // The last range has been flushed or we have collided with the
  1408. // mapped page writer. Regardless, exit the top FOR loop
  1409. // and return.
  1410. //
  1411. break;
  1412. }
  1413. Subsection = Subsection->NextSubsection;
  1414. PointerPte = Subsection->SubsectionBase;
  1415. } //end for
  1416. ASSERT (LastWritten == NULL);
  1417. ControlArea->FlushInProgressCount -= 1;
  1418. if ((ControlArea->u.Flags.CollidedFlush == 1) &&
  1419. (ControlArea->FlushInProgressCount == 0)) {
  1420. ControlArea->u.Flags.CollidedFlush = 0;
  1421. KePulseEvent (&MmCollidedFlushEvent, 0, FALSE);
  1422. }
  1423. UNLOCK_PFN (OldIrql);
  1424. if (Bail == TRUE) {
  1425. //
  1426. // This routine collided with the mapped page writer and the caller
  1427. // expects an error for this. Give it to him.
  1428. //
  1429. return STATUS_MAPPED_WRITER_COLLISION;
  1430. }
  1431. return IoStatus->Status;
  1432. }
  1433. BOOLEAN
  1434. MmPurgeSection (
  1435. IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
  1436. IN PLARGE_INTEGER Offset,
  1437. IN SIZE_T RegionSize,
  1438. IN ULONG IgnoreCacheViews
  1439. )
  1440. /*++
  1441. Routine Description:
  1442. This function determines if any views of the specified section
  1443. are mapped, and if not, purges valid pages (even modified ones)
  1444. from the specified section and returns any used pages to the free
  1445. list. This is accomplished by examining the prototype PTEs
  1446. from the specified offset to the end of the section, and if
  1447. any prototype PTEs are in the transition state, putting the
  1448. prototype PTE back into its original state and putting the
  1449. physical page on the free list.
  1450. NOTE:
  1451. If there is an I/O operation ongoing for one of the pages,
  1452. that page is eliminated from the segment and allowed to "float"
  1453. until the i/o is complete. Once the share count goes to zero
  1454. the page will be added to the free page list.
  1455. Arguments:
  1456. SectionObjectPointer - Supplies a pointer to the section objects.
  1457. Offset - Supplies the offset into the section in which to begin
  1458. purging pages. If this argument is not present, then the
  1459. whole section is purged without regard to the region size
  1460. argument.
  1461. RegionSize - Supplies the size of the region to purge. If this
  1462. is specified as zero and Offset is specified, the
  1463. region from Offset to the end of the file is purged.
  1464. Note: The largest value acceptable for RegionSize is
  1465. 0xFFFF0000;
  1466. IgnoreCacheViews - Supplies FALSE if mapped views in the system
  1467. cache should cause the function to return FALSE.
  1468. This is the normal case.
  1469. Supplies TRUE if mapped views should be ignored
  1470. and the flush should occur. NOTE THAT IF TRUE
  1471. IS SPECIFIED AND ANY DATA PURGED IS CURRENTLY MAPPED
  1472. AND VALID A BUGCHECK WILL OCCUR!!
  1473. Return Value:
  1474. Returns TRUE if either no section exists for the file object or
  1475. the section is not mapped and the purge was done, FALSE otherwise.
  1476. Note that FALSE is returned if during the purge operation, a page
  1477. could not be purged due to a non-zero reference count.
  1478. --*/
  1479. {
  1480. LOGICAL DroppedPfnLock;
  1481. PCONTROL_AREA ControlArea;
  1482. PMAPPED_FILE_SEGMENT Segment;
  1483. PMMPTE PointerPte;
  1484. PMMPTE LastPte;
  1485. PMMPTE FinalPte;
  1486. MMPTE PteContents;
  1487. PMMPFN Pfn1;
  1488. PMMPFN Pfn2;
  1489. KIRQL OldIrql;
  1490. ULONG PteOffset;
  1491. ULONG LastPteOffset;
  1492. PMSUBSECTION MappedSubsection;
  1493. PSUBSECTION Subsection;
  1494. PSUBSECTION FirstSubsection;
  1495. PSUBSECTION LastSubsection;
  1496. PSUBSECTION TempSubsection;
  1497. PSUBSECTION LastSubsectionWithProtos;
  1498. LARGE_INTEGER LocalOffset;
  1499. LOGICAL LockHeld;
  1500. BOOLEAN ReturnValue;
  1501. PFN_NUMBER PageFrameIndex;
  1502. PFN_NUMBER PageTableFrameIndex;
  1503. #if DBG
  1504. PFN_NUMBER LastLocked = 0;
  1505. #endif
  1506. //
  1507. // This is needed in case a page is on the mapped page writer list -
  1508. // the PFN lock will need to be released and APCs disabled.
  1509. //
  1510. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  1511. //
  1512. // Capture caller's file size, since we may modify it.
  1513. //
  1514. if (ARGUMENT_PRESENT(Offset)) {
  1515. LocalOffset = *Offset;
  1516. Offset = &LocalOffset;
  1517. }
  1518. //
  1519. // See if we can truncate this file to where the caller wants
  1520. // us to.
  1521. //
  1522. if (!MiCanFileBeTruncatedInternal(SectionObjectPointer, Offset, TRUE, &OldIrql)) {
  1523. return FALSE;
  1524. }
  1525. //
  1526. // PFN LOCK IS NOW HELD!
  1527. //
  1528. ControlArea = (PCONTROL_AREA)(SectionObjectPointer->DataSectionObject);
  1529. if ((ControlArea == NULL) || (ControlArea->u.Flags.Rom)) {
  1530. UNLOCK_PFN (OldIrql);
  1531. return TRUE;
  1532. }
  1533. //
  1534. // Even though MiCanFileBeTruncatedInternal returned TRUE, there could
  1535. // still be a system cache mapped view. We cannot truncate if
  1536. // the Cache Manager has a view mapped.
  1537. //
  1538. if ((IgnoreCacheViews == FALSE) &&
  1539. (ControlArea->NumberOfSystemCacheViews != 0)) {
  1540. UNLOCK_PFN (OldIrql);
  1541. return FALSE;
  1542. }
  1543. #if 0
  1544. //
  1545. // Prevent races when the control area is being deleted as the clean
  1546. // path releases the PFN lock midway through. File objects may still have
  1547. // section object pointers and data section objects that point at this
  1548. // control area, hence the purge can be issued.
  1549. //
  1550. // Check for this and fail the purge as the control area (and the section
  1551. // object pointers/data section objects) will be going away momentarily.
  1552. // Note that even though drivers have these data section objects, no one
  1553. // currently has an open section for this control area and no one is
  1554. // allowed to open one until the clean path finishes.
  1555. //
  1556. if (ControlArea->u.Flags.BeingDeleted == 1) {
  1557. UNLOCK_PFN (OldIrql);
  1558. return FALSE;
  1559. }
  1560. #else
  1561. //
  1562. // The above check can be removed as MiCanFileBeTruncatedInternal does
  1563. // the same check, so just assert it below.
  1564. //
  1565. ASSERT (ControlArea->u.Flags.BeingDeleted == 0);
  1566. #endif
  1567. //
  1568. // Purge the section - locate the subsection which
  1569. // contains the PTEs.
  1570. //
  1571. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  1572. Subsection = (PSUBSECTION)(ControlArea + 1);
  1573. if (!ARGUMENT_PRESENT (Offset)) {
  1574. //
  1575. // If the offset is not specified, flush the complete file ignoring
  1576. // the region size.
  1577. //
  1578. PteOffset = 0;
  1579. RegionSize = 0;
  1580. }
  1581. else {
  1582. PteOffset = (ULONG)(Offset->QuadPart >> PAGE_SHIFT);
  1583. //
  1584. // Make sure the PTEs are not in the extended part of the segment.
  1585. //
  1586. while (PteOffset >= Subsection->PtesInSubsection) {
  1587. PteOffset -= Subsection->PtesInSubsection;
  1588. Subsection = Subsection->NextSubsection;
  1589. if (Subsection == NULL) {
  1590. //
  1591. // The offset must be equal to the size of
  1592. // the section, don't purge anything just return.
  1593. //
  1594. UNLOCK_PFN (OldIrql);
  1595. return TRUE;
  1596. }
  1597. }
  1598. ASSERT (PteOffset < Subsection->PtesInSubsection);
  1599. }
  1600. //
  1601. // Locate the address of the last prototype PTE to be flushed.
  1602. //
  1603. if (RegionSize == 0) {
  1604. //
  1605. // Flush to end of section.
  1606. //
  1607. LastSubsection = Subsection;
  1608. Segment = (PMAPPED_FILE_SEGMENT) ControlArea->Segment;
  1609. if (MmIsAddressValid (Segment)) {
  1610. if (Segment->LastSubsectionHint != NULL) {
  1611. LastSubsection = (PSUBSECTION) Segment->LastSubsectionHint;
  1612. }
  1613. }
  1614. while (LastSubsection->NextSubsection != NULL) {
  1615. LastSubsection = LastSubsection->NextSubsection;
  1616. }
  1617. LastPteOffset = LastSubsection->PtesInSubsection - 1;
  1618. }
  1619. else {
  1620. //
  1621. // Calculate the end of the region.
  1622. //
  1623. LastPteOffset = PteOffset +
  1624. (ULONG) (((RegionSize + BYTE_OFFSET(Offset->LowPart)) - 1) >> PAGE_SHIFT);
  1625. LastSubsection = Subsection;
  1626. while (LastPteOffset >= LastSubsection->PtesInSubsection) {
  1627. LastPteOffset -= LastSubsection->PtesInSubsection;
  1628. if (LastSubsection->NextSubsection == NULL) {
  1629. LastPteOffset = LastSubsection->PtesInSubsection - 1;
  1630. break;
  1631. }
  1632. LastSubsection = LastSubsection->NextSubsection;
  1633. }
  1634. ASSERT (LastPteOffset < LastSubsection->PtesInSubsection);
  1635. }
  1636. //
  1637. // Try for the fast reference on the first and last subsection.
  1638. // If that cannot be gotten, then there are no prototype PTEs for this
  1639. // subsection, therefore there is nothing in it to flush so leap forwards.
  1640. //
  1641. // Note that subsections in between do not need referencing as
  1642. // the purge is smart enough to skip them if they're nonresident.
  1643. //
  1644. if (MiReferenceSubsection ((PMSUBSECTION)Subsection) == FALSE) {
  1645. do {
  1646. //
  1647. // If this increment would put us past the end offset, then nothing
  1648. // to flush, just return success.
  1649. //
  1650. if (Subsection == LastSubsection) {
  1651. UNLOCK_PFN (OldIrql);
  1652. return TRUE;
  1653. }
  1654. Subsection = Subsection->NextSubsection;
  1655. //
  1656. // If this increment put us past the end of section, then nothing
  1657. // to flush, just return success.
  1658. //
  1659. if (Subsection == NULL) {
  1660. UNLOCK_PFN (OldIrql);
  1661. return TRUE;
  1662. }
  1663. if (MiReferenceSubsection ((PMSUBSECTION)Subsection) == FALSE) {
  1664. continue;
  1665. }
  1666. //
  1667. // Start the flush at this subsection which is now referenced.
  1668. //
  1669. PointerPte = &Subsection->SubsectionBase[0];
  1670. break;
  1671. } while (TRUE);
  1672. }
  1673. else {
  1674. PointerPte = &Subsection->SubsectionBase[PteOffset];
  1675. }
  1676. FirstSubsection = Subsection;
  1677. ASSERT (Subsection->SubsectionBase != NULL);
  1678. //
  1679. // The first subsection is referenced, now reference count the last one.
  1680. // If the first is the last, just double reference it anyway as it
  1681. // simplifies cleanup later.
  1682. //
  1683. if (MiReferenceSubsection ((PMSUBSECTION)LastSubsection) == FALSE) {
  1684. ASSERT (Subsection != LastSubsection);
  1685. TempSubsection = Subsection->NextSubsection;
  1686. LastSubsectionWithProtos = NULL;
  1687. while (TempSubsection != LastSubsection) {
  1688. //
  1689. // If this increment put us past the end of section, then nothing
  1690. // to flush, just return success.
  1691. //
  1692. ASSERT (TempSubsection != NULL);
  1693. if ((PMSUBSECTION)TempSubsection->SubsectionBase != NULL) {
  1694. LastSubsectionWithProtos = TempSubsection;
  1695. }
  1696. TempSubsection = TempSubsection->NextSubsection;
  1697. }
  1698. //
  1699. // End the flush at this subsection and reference it.
  1700. //
  1701. if (LastSubsectionWithProtos == NULL) {
  1702. ASSERT (Subsection != NULL);
  1703. ASSERT (Subsection->SubsectionBase != NULL);
  1704. TempSubsection = Subsection;
  1705. }
  1706. else {
  1707. TempSubsection = LastSubsectionWithProtos;
  1708. }
  1709. if (MiReferenceSubsection ((PMSUBSECTION)TempSubsection) == FALSE) {
  1710. ASSERT (FALSE);
  1711. }
  1712. ASSERT (TempSubsection->SubsectionBase != NULL);
  1713. LastSubsection = TempSubsection;
  1714. LastPteOffset = LastSubsection->PtesInSubsection - 1;
  1715. }
  1716. //
  1717. // End the flush at this subsection which is now referenced.
  1718. //
  1719. // Point final PTE to 1 beyond the end.
  1720. //
  1721. FinalPte = &LastSubsection->SubsectionBase[LastPteOffset + 1];
  1722. //
  1723. // Increment the number of mapped views to
  1724. // prevent the section from being deleted while the purge is
  1725. // in progress.
  1726. //
  1727. ControlArea->NumberOfMappedViews += 1;
  1728. //
  1729. // Set being purged so no one can map a view
  1730. // while the purge is going on.
  1731. //
  1732. ControlArea->u.Flags.BeingPurged = 1;
  1733. ControlArea->u.Flags.WasPurged = 1;
  1734. LockHeld = TRUE;
  1735. ReturnValue = TRUE;
  1736. for (;;) {
  1737. if (!LockHeld) {
  1738. LockHeld = TRUE;
  1739. LOCK_PFN (OldIrql);
  1740. }
  1741. if (LastSubsection != Subsection) {
  1742. //
  1743. // Flush to the last PTE in this subsection.
  1744. //
  1745. LastPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
  1746. }
  1747. else {
  1748. //
  1749. // Flush to the end of the range.
  1750. //
  1751. LastPte = FinalPte;
  1752. }
  1753. if (Subsection->SubsectionBase == NULL) {
  1754. //
  1755. // The prototype PTEs for this subsection have either never been
  1756. // created or have been tossed due to memory pressure. Either
  1757. // way, this range can be skipped as there are obviously no
  1758. // pages to purge in this range.
  1759. //
  1760. ASSERT (LockHeld);
  1761. UNLOCK_PFN (OldIrql);
  1762. LockHeld = FALSE;
  1763. goto nextrange;
  1764. }
  1765. //
  1766. // Up the number of mapped views to prevent other threads
  1767. // from freeing this to the unused subsection list while we're
  1768. // operating on it.
  1769. //
  1770. MappedSubsection = (PMSUBSECTION) Subsection;
  1771. MappedSubsection->NumberOfMappedViews += 1;
  1772. if (MappedSubsection->DereferenceList.Flink != NULL) {
  1773. //
  1774. // Remove this from the list of unused subsections.
  1775. //
  1776. RemoveEntryList (&MappedSubsection->DereferenceList);
  1777. MI_UNUSED_SUBSECTIONS_COUNT_REMOVE (MappedSubsection);
  1778. MappedSubsection->DereferenceList.Flink = NULL;
  1779. }
  1780. //
  1781. // Set the access bit so an already ongoing trim won't blindly
  1782. // delete the prototype PTEs on completion of a mapped write.
  1783. // This can happen if the current thread dirties some pages and
  1784. // then deletes the view before the trim write finishes - this
  1785. // bit informs the trimming thread that a rescan is needed so
  1786. // that writes are not lost.
  1787. //
  1788. MappedSubsection->u2.SubsectionFlags2.SubsectionAccessed = 1;
  1789. //
  1790. // If the page table page containing the PTEs is not
  1791. // resident, then no PTEs can be in the valid or transition
  1792. // state! Skip over the PTEs.
  1793. //
  1794. if (!MiCheckProtoPtePageState(PointerPte, LockHeld, &DroppedPfnLock)) {
  1795. PointerPte = (PMMPTE)(((ULONG_PTR)PointerPte | (PAGE_SIZE - 1)) + 1);
  1796. }
  1797. while (PointerPte < LastPte) {
  1798. //
  1799. // If the page table page containing the PTEs is not
  1800. // resident, then no PTEs can be in the valid or transition
  1801. // state! Skip over the PTEs.
  1802. //
  1803. if (MiIsPteOnPdeBoundary(PointerPte)) {
  1804. if (!MiCheckProtoPtePageState(PointerPte, LockHeld, &DroppedPfnLock)) {
  1805. PointerPte = (PMMPTE)((PCHAR)PointerPte + PAGE_SIZE);
  1806. continue;
  1807. }
  1808. }
  1809. PteContents = *PointerPte;
  1810. if (PteContents.u.Hard.Valid == 1) {
  1811. //
  1812. // A valid PTE was found, it must be mapped in the
  1813. // system cache. Just exit the loop and return FALSE
  1814. // and let the caller fix this.
  1815. //
  1816. ReturnValue = FALSE;
  1817. break;
  1818. }
  1819. if ((PteContents.u.Soft.Prototype == 0) &&
  1820. (PteContents.u.Soft.Transition == 1)) {
  1821. if (!LockHeld) {
  1822. LockHeld = TRUE;
  1823. LOCK_PFN (OldIrql);
  1824. MiMakeSystemAddressValidPfn (PointerPte);
  1825. continue;
  1826. }
  1827. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE(&PteContents);
  1828. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1829. if ((Pfn1->OriginalPte.u.Soft.Prototype != 1) ||
  1830. (Pfn1->OriginalPte.u.Hard.Valid != 0) ||
  1831. (Pfn1->PteAddress != PointerPte)) {
  1832. //
  1833. // The pool containing the prototype PTEs has been
  1834. // corrupted. Pool corruption like this is fatal.
  1835. //
  1836. KeBugCheckEx (POOL_CORRUPTION_IN_FILE_AREA,
  1837. 0x2,
  1838. (ULONG_PTR)PointerPte,
  1839. (ULONG_PTR)Pfn1->PteAddress,
  1840. (ULONG_PTR)PteContents.u.Long);
  1841. }
  1842. #if DBG
  1843. if ((Pfn1->u3.e2.ReferenceCount != 0) &&
  1844. (Pfn1->u3.e1.WriteInProgress == 0)) {
  1845. //
  1846. // There must be an I/O in progress on this page.
  1847. //
  1848. if (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE(&PteContents) != LastLocked) {
  1849. UNLOCK_PFN (OldIrql);
  1850. LastLocked = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
  1851. LOCK_PFN (OldIrql);
  1852. MiMakeSystemAddressValidPfn (PointerPte);
  1853. continue;
  1854. }
  1855. }
  1856. #endif //DBG
  1857. //
  1858. // If the modified page writer has page locked for I/O
  1859. // wait for the I/O's to be completed and the pages
  1860. // to be unlocked. The eliminates a race condition
  1861. // when the modified page writer locks the pages, then
  1862. // a purge occurs and completes before the mapped
  1863. // writer thread runs.
  1864. //
  1865. if (Pfn1->u3.e1.WriteInProgress == 1) {
  1866. //
  1867. // A 3 or more thread deadlock can occur where:
  1868. //
  1869. // 1. The mapped page writer thread has issued a write
  1870. // and is in the filesystem code waiting for a resource.
  1871. //
  1872. // 2. Thread 2 owns the resource above but is waiting for
  1873. // the filesystem's quota mutex.
  1874. //
  1875. // 3. Thread 3 owns the quota mutex and is right here
  1876. // doing a purge from the cache manager when he notices
  1877. // the page to be purged is either already being written
  1878. // or is in the mapped page writer list. If it is
  1879. // already being written everything will unjam. If it
  1880. // is still on the mapped page writer list awaiting
  1881. // processing, then it must be cancelled - otherwise
  1882. // if this thread were to wait, deadlock can occur.
  1883. //
  1884. // The alternative to all this is for the filesystems to
  1885. // always release the quota mutex before purging but the
  1886. // filesystem overhead to do this is substantial.
  1887. //
  1888. if (MiCancelWriteOfMappedPfn (PageFrameIndex) == TRUE) {
  1889. //
  1890. // Stopping any failed writes (even deliberately
  1891. // cancelled ones) automatically cause a delay. A
  1892. // successful stop also results in the PFN lock
  1893. // being released and reacquired. So loop back to
  1894. // the top now as the world may have changed.
  1895. //
  1896. MiMakeSystemAddressValidPfn (PointerPte);
  1897. continue;
  1898. }
  1899. ASSERT (ControlArea->ModifiedWriteCount != 0);
  1900. ASSERT (Pfn1->u3.e2.ReferenceCount != 0);
  1901. ControlArea->u.Flags.SetMappedFileIoComplete = 1;
  1902. //
  1903. // Keep APCs blocked so no special APCs can be delivered
  1904. // in KeWait which would cause the dispatcher lock to be
  1905. // released opening a window where this thread could miss
  1906. // a pulse.
  1907. //
  1908. UNLOCK_PFN_AND_THEN_WAIT (APC_LEVEL);
  1909. KeWaitForSingleObject (&MmMappedFileIoComplete,
  1910. WrPageOut,
  1911. KernelMode,
  1912. FALSE,
  1913. NULL);
  1914. KeLowerIrql (OldIrql);
  1915. LOCK_PFN (OldIrql);
  1916. MiMakeSystemAddressValidPfn (PointerPte);
  1917. continue;
  1918. }
  1919. if (Pfn1->u3.e1.ReadInProgress == 1) {
  1920. //
  1921. // The page currently is being read in from the
  1922. // disk. Treat this just like a valid PTE and
  1923. // return false.
  1924. //
  1925. ReturnValue = FALSE;
  1926. break;
  1927. }
  1928. ASSERT (!((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
  1929. (Pfn1->OriginalPte.u.Soft.Transition == 1)));
  1930. MI_WRITE_INVALID_PTE (PointerPte, Pfn1->OriginalPte);
  1931. ASSERT (Pfn1->OriginalPte.u.Hard.Valid == 0);
  1932. ControlArea->NumberOfPfnReferences -= 1;
  1933. ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0);
  1934. MiUnlinkPageFromList (Pfn1);
  1935. MI_SET_PFN_DELETED (Pfn1);
  1936. PageTableFrameIndex = Pfn1->u4.PteFrame;
  1937. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  1938. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  1939. //
  1940. // If the reference count for the page is zero, insert
  1941. // it into the free page list, otherwise leave it alone
  1942. // and when the reference count is decremented to zero
  1943. // the page will go to the free list.
  1944. //
  1945. if (Pfn1->u3.e2.ReferenceCount == 0) {
  1946. MiReleasePageFileSpace (Pfn1->OriginalPte);
  1947. MiInsertPageInFreeList (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents));
  1948. }
  1949. }
  1950. PointerPte += 1;
  1951. if ((MiIsPteOnPdeBoundary(PointerPte)) && (LockHeld)) {
  1952. //
  1953. // Unlock PFN so large requests will not block other
  1954. // threads on MP systems.
  1955. //
  1956. UNLOCK_PFN (OldIrql);
  1957. LockHeld = FALSE;
  1958. }
  1959. }
  1960. if (!LockHeld) {
  1961. LockHeld = TRUE;
  1962. LOCK_PFN (OldIrql);
  1963. }
  1964. ASSERT (MappedSubsection->DereferenceList.Flink == NULL);
  1965. ASSERT ((MappedSubsection->NumberOfMappedViews >= 1) ||
  1966. (MappedSubsection->u.SubsectionFlags.SubsectionStatic == 1));
  1967. MappedSubsection->NumberOfMappedViews -= 1;
  1968. if ((MappedSubsection->NumberOfMappedViews == 0) &&
  1969. (MappedSubsection->u.SubsectionFlags.SubsectionStatic == 0)) {
  1970. //
  1971. // Insert this subsection into the unused subsection list.
  1972. //
  1973. InsertTailList (&MmUnusedSubsectionList,
  1974. &MappedSubsection->DereferenceList);
  1975. MI_UNUSED_SUBSECTIONS_COUNT_INSERT (MappedSubsection);
  1976. }
  1977. ASSERT (LockHeld);
  1978. UNLOCK_PFN (OldIrql);
  1979. LockHeld = FALSE;
  1980. nextrange:
  1981. if ((LastSubsection != Subsection) && (ReturnValue)) {
  1982. //
  1983. // Get the next subsection in the list.
  1984. //
  1985. Subsection = Subsection->NextSubsection;
  1986. PointerPte = Subsection->SubsectionBase;
  1987. }
  1988. else {
  1989. //
  1990. // The last range has been flushed, exit the top FOR loop
  1991. // and return.
  1992. //
  1993. break;
  1994. }
  1995. }
  1996. if (!LockHeld) {
  1997. LOCK_PFN (OldIrql);
  1998. }
  1999. MiDecrementSubsections (FirstSubsection, FirstSubsection);
  2000. MiDecrementSubsections (LastSubsection, LastSubsection);
  2001. ASSERT ((LONG)ControlArea->NumberOfMappedViews >= 1);
  2002. ControlArea->NumberOfMappedViews -= 1;
  2003. ControlArea->u.Flags.BeingPurged = 0;
  2004. //
  2005. // Check to see if the control area should be deleted. This
  2006. // will release the PFN lock.
  2007. //
  2008. MiCheckControlArea (ControlArea, NULL, OldIrql);
  2009. return ReturnValue;
  2010. }
  2011. BOOLEAN
  2012. MmFlushImageSection (
  2013. IN PSECTION_OBJECT_POINTERS SectionPointer,
  2014. IN MMFLUSH_TYPE FlushType
  2015. )
  2016. /*++
  2017. Routine Description:
  2018. This function determines if any views of the specified image section
  2019. are mapped, and if not, flushes valid pages (even modified ones)
  2020. from the specified section and returns any used pages to the free
  2021. list. This is accomplished by examining the prototype PTEs
  2022. from the specified offset to the end of the section, and if
  2023. any prototype PTEs are in the transition state, putting the
  2024. prototype PTE back into its original state and putting the
  2025. physical page on the free list.
  2026. Arguments:
  2027. SectionPointer - Supplies a pointer to a section object pointers
  2028. within the FCB.
  2029. FlushType - Supplies the type of flush to check for. One of
  2030. MmFlushForDelete or MmFlushForWrite.
  2031. Return Value:
  2032. Returns TRUE if either no section exists for the file object or
  2033. the section is not mapped and the purge was done, FALSE otherwise.
  2034. --*/
  2035. {
  2036. PLIST_ENTRY Next;
  2037. PCONTROL_AREA ControlArea;
  2038. PLARGE_CONTROL_AREA LargeControlArea;
  2039. KIRQL OldIrql;
  2040. LOGICAL state;
  2041. if (FlushType == MmFlushForDelete) {
  2042. //
  2043. // Do a quick check to see if there are any mapped views for
  2044. // the data section. If so, just return FALSE.
  2045. //
  2046. LOCK_PFN (OldIrql);
  2047. ControlArea = (PCONTROL_AREA)(SectionPointer->DataSectionObject);
  2048. if (ControlArea != NULL) {
  2049. if ((ControlArea->NumberOfUserReferences != 0) ||
  2050. (ControlArea->u.Flags.BeingCreated)) {
  2051. UNLOCK_PFN (OldIrql);
  2052. return FALSE;
  2053. }
  2054. }
  2055. UNLOCK_PFN (OldIrql);
  2056. }
  2057. //
  2058. // Check the status of the control area. If the control area is in use
  2059. // or the control area is being deleted, this operation cannot continue.
  2060. //
  2061. state = MiCheckControlAreaStatus (CheckImageSection,
  2062. SectionPointer,
  2063. FALSE,
  2064. &ControlArea,
  2065. &OldIrql);
  2066. if (ControlArea == NULL) {
  2067. return (BOOLEAN) state;
  2068. }
  2069. //
  2070. // PFN LOCK IS NOW HELD!
  2071. //
  2072. //
  2073. // Repeat until there are no more control areas - multiple control areas
  2074. // for the same image section occur to support user global DLLs - these DLLs
  2075. // require data that is shared within a session but not across sessions.
  2076. // Note this can only happen for Hydra.
  2077. //
  2078. do {
  2079. //
  2080. // Set the being deleted flag and up the number of mapped views
  2081. // for the segment. Upping the number of mapped views prevents
  2082. // the segment from being deleted and passed to the deletion thread
  2083. // while we are forcing a delete.
  2084. //
  2085. ControlArea->u.Flags.BeingDeleted = 1;
  2086. ControlArea->NumberOfMappedViews = 1;
  2087. LargeControlArea = NULL;
  2088. if (ControlArea->u.Flags.GlobalOnlyPerSession == 0) {
  2089. NOTHING;
  2090. }
  2091. else if (IsListEmpty(&((PLARGE_CONTROL_AREA)ControlArea)->UserGlobalList)) {
  2092. ASSERT (ControlArea ==
  2093. (PCONTROL_AREA)SectionPointer->ImageSectionObject);
  2094. }
  2095. else {
  2096. //
  2097. // Check if there's only one image section in this control area, so
  2098. // we don't reference the section object pointers as the
  2099. // MiCleanSection call may result in its deletion.
  2100. //
  2101. //
  2102. // There are multiple control areas, bump the reference count
  2103. // on one of them (doesn't matter which one) so that it can't
  2104. // go away. This ensures the section object pointers will stick
  2105. // around even after the calls below so we can safely reloop to
  2106. // flush any other remaining control areas.
  2107. //
  2108. ASSERT (ControlArea->u.Flags.GlobalOnlyPerSession == 1);
  2109. Next = ((PLARGE_CONTROL_AREA)ControlArea)->UserGlobalList.Flink;
  2110. LargeControlArea = CONTAINING_RECORD (Next,
  2111. LARGE_CONTROL_AREA,
  2112. UserGlobalList);
  2113. ASSERT (LargeControlArea->u.Flags.GlobalOnlyPerSession == 1);
  2114. LargeControlArea->NumberOfSectionReferences += 1;
  2115. }
  2116. //
  2117. // This is a page file backed or image segment. The segment is being
  2118. // deleted, remove all references to the paging file and physical
  2119. // memory.
  2120. //
  2121. UNLOCK_PFN (OldIrql);
  2122. MiCleanSection (ControlArea, TRUE);
  2123. //
  2124. // Get the next Hydra control area.
  2125. //
  2126. if (LargeControlArea != NULL) {
  2127. state = MiCheckControlAreaStatus (CheckImageSection,
  2128. SectionPointer,
  2129. FALSE,
  2130. &ControlArea,
  2131. &OldIrql);
  2132. if (!ControlArea) {
  2133. LOCK_PFN (OldIrql);
  2134. LargeControlArea->NumberOfSectionReferences -= 1;
  2135. MiCheckControlArea ((PCONTROL_AREA)LargeControlArea,
  2136. NULL,
  2137. OldIrql);
  2138. }
  2139. else {
  2140. LargeControlArea->NumberOfSectionReferences -= 1;
  2141. MiCheckControlArea ((PCONTROL_AREA)LargeControlArea,
  2142. NULL,
  2143. OldIrql);
  2144. LOCK_PFN (OldIrql);
  2145. }
  2146. }
  2147. else {
  2148. state = TRUE;
  2149. break;
  2150. }
  2151. } while (ControlArea);
  2152. return (BOOLEAN) state;
  2153. }
  2154. VOID
  2155. MiFlushDirtyBitsToPfn (
  2156. IN PMMPTE PointerPte,
  2157. IN PMMPTE LastPte,
  2158. IN PEPROCESS Process,
  2159. IN BOOLEAN SystemCache
  2160. )
  2161. {
  2162. KIRQL OldIrql;
  2163. MMPTE PteContents;
  2164. PMMPFN Pfn1;
  2165. PVOID Va;
  2166. PMMPTE PointerPde;
  2167. PMMPTE PointerPpe;
  2168. PMMPTE PointerPxe;
  2169. ULONG Waited;
  2170. Va = MiGetVirtualAddressMappedByPte (PointerPte);
  2171. LOCK_PFN (OldIrql);
  2172. while (PointerPte <= LastPte) {
  2173. PteContents = *PointerPte;
  2174. if ((PteContents.u.Hard.Valid == 1) &&
  2175. (MI_IS_PTE_DIRTY (PteContents))) {
  2176. //
  2177. // Flush the modify bit to the PFN database.
  2178. //
  2179. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  2180. MI_SET_MODIFIED (Pfn1, 1, 0x2);
  2181. MI_SET_PTE_CLEAN (PteContents);
  2182. //
  2183. // No need to capture the PTE contents as we are going to
  2184. // write the page anyway and the Modify bit will be cleared
  2185. // before the write is done.
  2186. //
  2187. (VOID)KeFlushSingleTb (Va,
  2188. FALSE,
  2189. SystemCache,
  2190. (PHARDWARE_PTE)PointerPte,
  2191. PteContents.u.Flush);
  2192. }
  2193. Va = (PVOID)((PCHAR)Va + PAGE_SIZE);
  2194. PointerPte += 1;
  2195. if (MiIsPteOnPdeBoundary (PointerPte)) {
  2196. PointerPde = MiGetPteAddress (PointerPte);
  2197. while (PointerPte <= LastPte) {
  2198. PointerPxe = MiGetPdeAddress (PointerPde);
  2199. PointerPpe = MiGetPteAddress (PointerPde);
  2200. if (!MiDoesPxeExistAndMakeValid (PointerPxe,
  2201. Process,
  2202. TRUE,
  2203. &Waited)) {
  2204. //
  2205. // No page directory parent page exists for this address.
  2206. //
  2207. PointerPxe += 1;
  2208. PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
  2209. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  2210. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  2211. }
  2212. else if (!MiDoesPpeExistAndMakeValid (PointerPpe,
  2213. Process,
  2214. TRUE,
  2215. &Waited)) {
  2216. //
  2217. // No page directory page exists for this address.
  2218. //
  2219. PointerPpe += 1;
  2220. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  2221. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  2222. }
  2223. else {
  2224. Waited = 0;
  2225. if (!MiDoesPdeExistAndMakeValid (PointerPde,
  2226. Process,
  2227. TRUE,
  2228. &Waited)) {
  2229. //
  2230. // No page table page exists for this address.
  2231. //
  2232. PointerPde += 1;
  2233. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  2234. }
  2235. else {
  2236. //
  2237. // If the PFN lock (and accordingly the WS mutex) was
  2238. // released and reacquired we must retry the operation.
  2239. //
  2240. if (Waited != 0) {
  2241. continue;
  2242. }
  2243. //
  2244. // The PFN lock has been held since we acquired the
  2245. // page directory parent, ie: this PTE we can operate on
  2246. // immediately.
  2247. //
  2248. break;
  2249. }
  2250. }
  2251. }
  2252. Va = MiGetVirtualAddressMappedByPte (PointerPte);
  2253. }
  2254. }
  2255. UNLOCK_PFN (OldIrql);
  2256. return;
  2257. }
  2258. PSUBSECTION
  2259. MiGetSystemCacheSubsection (
  2260. IN PVOID BaseAddress,
  2261. OUT PMMPTE *ProtoPte
  2262. )
  2263. {
  2264. KIRQL OldIrql;
  2265. PMMPTE PointerPte;
  2266. PSUBSECTION Subsection;
  2267. PointerPte = MiGetPteAddress (BaseAddress);
  2268. LOCK_PFN (OldIrql);
  2269. Subsection = MiGetSubsectionAndProtoFromPte (PointerPte, ProtoPte);
  2270. UNLOCK_PFN (OldIrql);
  2271. return Subsection;
  2272. }
  2273. LOGICAL
  2274. MiCheckProtoPtePageState (
  2275. IN PMMPTE PrototypePte,
  2276. IN LOGICAL PfnLockHeld,
  2277. OUT PLOGICAL DroppedPfnLock
  2278. )
  2279. /*++
  2280. Routine Description:
  2281. Checks the state of the page containing the specified
  2282. prototype PTE.
  2283. If the page is valid or transition and has transition or valid prototype
  2284. PTEs contained with it, TRUE is returned and the page is made valid
  2285. (if transition). Otherwise return FALSE indicating no prototype
  2286. PTEs within this page are of interest.
  2287. Arguments:
  2288. PrototypePte - Supplies a pointer to a prototype PTE within the page.
  2289. DroppedPfnLock - Supplies a pointer to a logical this routine sets to
  2290. TRUE if the PFN lock is released & reacquired.
  2291. Return Value:
  2292. TRUE if the page containing the proto PTE was made resident.
  2293. FALSE if otherwise.
  2294. --*/
  2295. {
  2296. PMMPTE PointerPte;
  2297. MMPTE PteContents;
  2298. PFN_NUMBER PageFrameIndex;
  2299. PMMPFN Pfn;
  2300. *DroppedPfnLock = FALSE;
  2301. #if (_MI_PAGING_LEVELS >= 3)
  2302. //
  2303. // First check whether the page directory page is present. Since there
  2304. // is no lazy loading of PPEs, the validity check alone is sufficient.
  2305. //
  2306. PointerPte = MiGetPdeAddress (PrototypePte);
  2307. PteContents = *PointerPte;
  2308. if (PteContents.u.Hard.Valid == 0) {
  2309. return FALSE;
  2310. }
  2311. #endif
  2312. PointerPte = MiGetPteAddress (PrototypePte);
  2313. #if (_MI_PAGING_LEVELS < 3)
  2314. if (PointerPte->u.Hard.Valid == 0) {
  2315. MiCheckPdeForPagedPool (PrototypePte);
  2316. }
  2317. #endif
  2318. PteContents = *PointerPte;
  2319. if (PteContents.u.Hard.Valid == 1) {
  2320. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
  2321. Pfn = MI_PFN_ELEMENT (PageFrameIndex);
  2322. if (Pfn->u2.ShareCount != 1) {
  2323. return TRUE;
  2324. }
  2325. }
  2326. else if ((PteContents.u.Soft.Prototype == 0) &&
  2327. (PteContents.u.Soft.Transition == 1)) {
  2328. //
  2329. // Transition, if on standby or modified, return FALSE.
  2330. //
  2331. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
  2332. Pfn = MI_PFN_ELEMENT (PageFrameIndex);
  2333. if (Pfn->u3.e1.PageLocation >= ActiveAndValid) {
  2334. if (PfnLockHeld) {
  2335. MiMakeSystemAddressValidPfn (PrototypePte);
  2336. *DroppedPfnLock = TRUE;
  2337. }
  2338. return TRUE;
  2339. }
  2340. }
  2341. //
  2342. // Page is not resident or is on standby / modified list.
  2343. //
  2344. return FALSE;
  2345. }