Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1755 lines
51 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. deleteva.c
  5. Abstract:
  6. This module contains the routines for deleting virtual address space.
  7. Author:
  8. Lou Perazzoli (loup) 11-May-1989
  9. Landy Wang (landyw) 02-June-1997
  10. --*/
  11. #include "mi.h"
  12. #if defined (_WIN64) && defined (DBG_VERBOSE)
  13. typedef struct _MI_TRACK_USE {
  14. PFN_NUMBER Pfn;
  15. PVOID Va;
  16. ULONG Id;
  17. ULONG PfnUse;
  18. ULONG PfnUseCounted;
  19. ULONG TickCount;
  20. PKTHREAD Thread;
  21. PEPROCESS Process;
  22. } MI_TRACK_USE, *PMI_TRACK_USE;
  23. ULONG MiTrackUseSize = 8192;
  24. PMI_TRACK_USE MiTrackUse;
  25. LONG MiTrackUseIndex;
  26. VOID
  27. MiInitUseCounts (
  28. VOID
  29. )
  30. {
  31. MiTrackUse = ExAllocatePoolWithTag (NonPagedPool,
  32. MiTrackUseSize * sizeof (MI_TRACK_USE),
  33. 'lqrI');
  34. ASSERT (MiTrackUse != NULL);
  35. }
  36. VOID
  37. MiCheckUseCounts (
  38. PVOID TempHandle,
  39. PVOID Va,
  40. ULONG Id
  41. )
  42. /*++
  43. Routine Description:
  44. This routine ensures that all the counters are correct.
  45. Arguments:
  46. TempHandle - Supplies the handle for used page table counts.
  47. Va - Supplies the virtual address.
  48. Id - Supplies the ID.
  49. Return Value:
  50. None.
  51. Environment:
  52. Kernel mode, called with APCs disabled, working set mutex and PFN lock
  53. held.
  54. --*/
  55. {
  56. LOGICAL LogIt;
  57. ULONG i;
  58. ULONG TempHandleCount;
  59. ULONG TempCounted;
  60. PMMPTE TempPage;
  61. KIRQL OldIrql;
  62. ULONG Index;
  63. PFN_NUMBER PageFrameIndex;
  64. PMI_TRACK_USE Information;
  65. LARGE_INTEGER TimeStamp;
  66. PMMPFN Pfn1;
  67. PEPROCESS Process;
  68. Process = PsGetCurrentProcess ();
  69. //
  70. // TempHandle is really the PMMPFN containing the UsedPageTableEntries.
  71. //
  72. Pfn1 = (PMMPFN)TempHandle;
  73. PageFrameIndex = MI_PFN_ELEMENT_TO_INDEX (Pfn1);
  74. TempHandleCount = MI_GET_USED_PTES_FROM_HANDLE (TempHandle);
  75. if (Id & 0x80000000) {
  76. ASSERT (TempHandleCount != 0);
  77. }
  78. TempPage = (PMMPTE) MiMapPageInHyperSpace (Process, PageFrameIndex, &OldIrql);
  79. TempCounted = 0;
  80. for (i = 0; i < PTE_PER_PAGE; i += 1) {
  81. if (TempPage->u.Long != 0) {
  82. TempCounted += 1;
  83. }
  84. TempPage += 1;
  85. }
  86. #if 0
  87. if (zz & 0x4) {
  88. LogIt = FALSE;
  89. if (Pfn1->PteFrame == PageFrameIndex) {
  90. // TopLevel parent page, not interesting to us.
  91. }
  92. else {
  93. PMMPFN Pfn2;
  94. Pfn2 = MI_PFN_ELEMENT (Pfn1->PteFrame);
  95. if (Pfn2->PteFrame == Pfn1->PteFrame) {
  96. // our parent is the toplevel, so very interesting.
  97. LogIt = TRUE;
  98. }
  99. }
  100. }
  101. else {
  102. LogIt = TRUE;
  103. }
  104. #else
  105. LogIt = TRUE;
  106. #endif
  107. if (LogIt == TRUE) {
  108. //
  109. // Capture information
  110. //
  111. Index = InterlockedExchangeAdd(&MiTrackUseIndex, 1);
  112. Index &= (MiTrackUseSize - 1);
  113. Information = &(MiTrackUse[Index]);
  114. Information->Thread = KeGetCurrentThread();
  115. Information->Process = (PEPROCESS)((ULONG_PTR)PsGetCurrentProcess () + KeGetCurrentProcessorNumber ());
  116. Information->Va = Va;
  117. Information->Id = Id;
  118. KeQueryTickCount(&TimeStamp);
  119. Information->TickCount = TimeStamp.LowPart;
  120. Information->Pfn = PageFrameIndex;
  121. Information->PfnUse = TempHandleCount;
  122. Information->PfnUseCounted = TempCounted;
  123. if (TempCounted != TempHandleCount) {
  124. DbgPrint ("MiCheckUseCounts %p %x %x %x %x\n", Va, Id, PageFrameIndex, TempHandleCount, TempCounted);
  125. DbgBreakPoint ();
  126. }
  127. }
  128. MiUnmapPageInHyperSpace (Process, TempPage, OldIrql);
  129. return;
  130. }
  131. #endif
  132. VOID
  133. MiDeleteVirtualAddresses (
  134. IN PUCHAR Va,
  135. IN PUCHAR EndingAddress,
  136. IN PMMVAD Vad
  137. )
  138. /*++
  139. Routine Description:
  140. This routine deletes the specified virtual address range within
  141. the current process.
  142. Arguments:
  143. Va - Supplies the first virtual address to delete.
  144. EndingAddress - Supplies the last address to delete.
  145. Vad - Supplies the virtual address descriptor which maps this range
  146. or NULL if we are not concerned about views. From the Vad the
  147. range of prototype PTEs is determined and this information is
  148. used to uncover if the PTE refers to a prototype PTE or a fork PTE.
  149. Return Value:
  150. None.
  151. Environment:
  152. Kernel mode, APC_LEVEL, called with address space and working set mutexes
  153. held. The working set mutex may be released and reacquired to fault
  154. pages in.
  155. --*/
  156. {
  157. PMMPFN Pfn1;
  158. PFN_NUMBER PageFrameIndex;
  159. WSLE_NUMBER WsPfnIndex;
  160. WSLE_NUMBER WorkingSetIndex;
  161. MMWSLENTRY Locked;
  162. WSLE_NUMBER Entry;
  163. ULONG InvalidPtes;
  164. LOGICAL PfnHeld;
  165. PVOID TempVa;
  166. PMMPTE PointerPte;
  167. PMMPTE PointerPde;
  168. PMMPTE PointerPpe;
  169. PMMPTE PointerPxe;
  170. PMMPTE ProtoPte;
  171. PMMPTE LastProtoPte;
  172. PMMPTE LastPte;
  173. PMMPTE LastPteThisPage;
  174. MMPTE TempPte;
  175. PEPROCESS CurrentProcess;
  176. PSUBSECTION Subsection;
  177. PVOID UsedPageTableHandle;
  178. KIRQL OldIrql;
  179. MMPTE_FLUSH_LIST FlushList;
  180. ULONG Waited;
  181. LOGICAL Skipped;
  182. LOGICAL AddressSpaceDeletion;
  183. #if DBG
  184. PMMPTE ProtoPte2;
  185. PMMPTE LastProtoPte2;
  186. PMMCLONE_BLOCK CloneBlock;
  187. PMMCLONE_DESCRIPTOR CloneDescriptor;
  188. #endif
  189. #if (_MI_PAGING_LEVELS >= 3)
  190. PVOID UsedPageDirectoryHandle;
  191. PVOID TempHandle;
  192. #endif
  193. FlushList.Count = 0;
  194. CurrentProcess = PsGetCurrentProcess ();
  195. PointerPpe = MiGetPpeAddress (Va);
  196. PointerPde = MiGetPdeAddress (Va);
  197. PointerPte = MiGetPteAddress (Va);
  198. PointerPxe = MiGetPxeAddress (Va);
  199. LastPte = MiGetPteAddress (EndingAddress);
  200. PfnHeld = FALSE;
  201. OldIrql = MM_NOIRQL;
  202. SATISFY_OVERZEALOUS_COMPILER (Subsection = NULL);
  203. if ((Vad == NULL) ||
  204. (Vad->u.VadFlags.PrivateMemory) ||
  205. (Vad->FirstPrototypePte == NULL)) {
  206. ProtoPte = NULL;
  207. LastProtoPte = NULL;
  208. }
  209. else {
  210. ProtoPte = Vad->FirstPrototypePte;
  211. LastProtoPte = (PMMPTE) 4;
  212. }
  213. if (CurrentProcess->CloneRoot == NULL) {
  214. AddressSpaceDeletion = TRUE;
  215. }
  216. else {
  217. AddressSpaceDeletion = FALSE;
  218. }
  219. do {
  220. //
  221. // Attempt to leap forward skipping over empty page directories
  222. // and page tables where possible.
  223. //
  224. #if (_MI_PAGING_LEVELS >= 3)
  225. restart:
  226. #endif
  227. Skipped = FALSE;
  228. while (MiDoesPxeExistAndMakeValid (PointerPxe,
  229. CurrentProcess,
  230. MM_NOIRQL,
  231. &Waited) == FALSE) {
  232. //
  233. // This extended page directory parent entry is empty,
  234. // go to the next one.
  235. //
  236. Skipped = TRUE;
  237. PointerPxe += 1;
  238. PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
  239. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  240. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  241. Va = MiGetVirtualAddressMappedByPte (PointerPte);
  242. if (Va > EndingAddress) {
  243. //
  244. // All done, return.
  245. //
  246. return;
  247. }
  248. }
  249. while (MiDoesPpeExistAndMakeValid (PointerPpe,
  250. CurrentProcess,
  251. MM_NOIRQL,
  252. &Waited) == FALSE) {
  253. //
  254. // This page directory parent entry is empty, go to the next one.
  255. //
  256. Skipped = TRUE;
  257. PointerPpe += 1;
  258. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  259. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  260. Va = MiGetVirtualAddressMappedByPte (PointerPte);
  261. if (Va > EndingAddress) {
  262. //
  263. // All done, return.
  264. //
  265. return;
  266. }
  267. #if (_MI_PAGING_LEVELS >= 4)
  268. if (MiIsPteOnPdeBoundary (PointerPpe)) {
  269. PointerPxe += 1;
  270. ASSERT (PointerPxe == MiGetPteAddress (PointerPpe));
  271. goto restart;
  272. }
  273. #endif
  274. }
  275. #if (_MI_PAGING_LEVELS >= 3) && defined (DBG)
  276. MI_CHECK_USED_PTES_HANDLE (PointerPte);
  277. TempHandle = MI_GET_USED_PTES_HANDLE (PointerPte);
  278. ASSERT ((MI_GET_USED_PTES_FROM_HANDLE (TempHandle) != 0) ||
  279. (PointerPde->u.Long == 0));
  280. #endif
  281. while (MiDoesPdeExistAndMakeValid (PointerPde,
  282. CurrentProcess,
  283. MM_NOIRQL,
  284. &Waited) == FALSE) {
  285. //
  286. // This page directory entry is empty, go to the next one.
  287. //
  288. Skipped = TRUE;
  289. PointerPde += 1;
  290. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  291. Va = MiGetVirtualAddressMappedByPte (PointerPte);
  292. if (Va > EndingAddress) {
  293. //
  294. // All done, return.
  295. //
  296. return;
  297. }
  298. #if (_MI_PAGING_LEVELS >= 3)
  299. if (MiIsPteOnPdeBoundary (PointerPde)) {
  300. PointerPpe += 1;
  301. ASSERT (PointerPpe == MiGetPteAddress (PointerPde));
  302. PointerPxe = MiGetPteAddress (PointerPpe);
  303. goto restart;
  304. }
  305. #endif
  306. #if DBG
  307. if ((LastProtoPte != NULL) &&
  308. (Vad->u2.VadFlags2.ExtendableFile == 0)) {
  309. ProtoPte2 = MiGetProtoPteAddress (Vad, MI_VA_TO_VPN (Va));
  310. Subsection = MiLocateSubsection (Vad,MI_VA_TO_VPN (Va));
  311. LastProtoPte2 = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
  312. if (Vad->u.VadFlags.ImageMap != 1) {
  313. if ((ProtoPte2 < Subsection->SubsectionBase) ||
  314. (ProtoPte2 >= LastProtoPte2)) {
  315. DbgPrint ("bad proto PTE %p va %p Vad %p sub %p\n",
  316. ProtoPte2,Va,Vad,Subsection);
  317. DbgBreakPoint();
  318. }
  319. }
  320. }
  321. #endif
  322. }
  323. //
  324. // The PPE and PDE are now valid, get the page table use address
  325. // as it changes whenever the PDE does.
  326. //
  327. #if (_MI_PAGING_LEVELS >= 4)
  328. ASSERT64 (PointerPxe->u.Hard.Valid == 1);
  329. #endif
  330. ASSERT64 (PointerPpe->u.Hard.Valid == 1);
  331. ASSERT (PointerPde->u.Hard.Valid == 1);
  332. ASSERT (Va <= EndingAddress);
  333. MI_CHECK_USED_PTES_HANDLE (Va);
  334. UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (Va);
  335. #if (_MI_PAGING_LEVELS >= 3) && defined (DBG)
  336. ASSERT ((MI_GET_USED_PTES_FROM_HANDLE (UsedPageTableHandle) != 0) ||
  337. (PointerPte->u.Long == 0));
  338. #endif
  339. //
  340. // If we skipped chunks of address space, the prototype PTE pointer
  341. // must be updated now so VADs that span multiple subsections
  342. // are handled properly.
  343. //
  344. if ((Skipped == TRUE) && (LastProtoPte != NULL)) {
  345. ProtoPte = MiGetProtoPteAddress (Vad, MI_VA_TO_VPN(Va));
  346. Subsection = MiLocateSubsection (Vad, MI_VA_TO_VPN(Va));
  347. if (Subsection != NULL) {
  348. LastProtoPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
  349. #if DBG
  350. if (Vad->u.VadFlags.ImageMap != 1) {
  351. if ((ProtoPte < Subsection->SubsectionBase) ||
  352. (ProtoPte >= LastProtoPte)) {
  353. DbgPrint ("bad proto PTE %p va %p Vad %p sub %p\n",
  354. ProtoPte,Va,Vad,Subsection);
  355. DbgBreakPoint();
  356. }
  357. }
  358. #endif
  359. }
  360. else {
  361. //
  362. // The Vad span is larger than the section being mapped.
  363. // Null the proto PTE local as no more proto PTEs will
  364. // need to be deleted at this point.
  365. //
  366. LastProtoPte = NULL;
  367. }
  368. }
  369. //
  370. // A valid address has been located, examine and delete each PTE.
  371. //
  372. InvalidPtes = 0;
  373. if (AddressSpaceDeletion == TRUE) {
  374. //
  375. // The working set mutex is held so no valid PTEs can be trimmed.
  376. // Take advantage of this fact and remove the WSLEs for all valid
  377. // PTEs now since the PFN lock is not held. This is only done
  378. // for non-forked processes as deletion of forked PTEs below may
  379. // drop the working set mutex which would introduce races wth
  380. // WSLE deletion being done here.
  381. //
  382. // The deleting of the PTEs will require the PFN lock.
  383. //
  384. ASSERT (CurrentProcess->CloneRoot == NULL);
  385. LastPteThisPage = (PMMPTE)(((ULONG_PTR)PointerPte | (PAGE_SIZE - 1)) + 1) - 1;
  386. if (LastPteThisPage > LastPte) {
  387. LastPteThisPage = LastPte;
  388. }
  389. TempVa = MiGetVirtualAddressMappedByPte (LastPteThisPage);
  390. do {
  391. TempPte = *LastPteThisPage;
  392. if (TempPte.u.Hard.Valid != 0) {
  393. #ifdef _X86_
  394. #if DBG
  395. #if !defined(NT_UP)
  396. if (TempPte.u.Hard.Writable == 1) {
  397. ASSERT (TempPte.u.Hard.Dirty == 1);
  398. }
  399. #endif //NTUP
  400. #endif //DBG
  401. #endif //X86
  402. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&TempPte);
  403. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  404. WsPfnIndex = Pfn1->u1.WsIndex;
  405. //
  406. // PTE is valid - find the WSLE for this page and eliminate it.
  407. //
  408. WorkingSetIndex = MiLocateWsle (TempVa,
  409. MmWorkingSetList,
  410. WsPfnIndex);
  411. ASSERT (WorkingSetIndex != WSLE_NULL_INDEX);
  412. //
  413. // Check to see if this entry is locked in the working set
  414. // or locked in memory.
  415. //
  416. Locked = MmWsle[WorkingSetIndex].u1.e1;
  417. MiRemoveWsle (WorkingSetIndex, MmWorkingSetList);
  418. //
  419. // Add this entry to the list of free working set entries
  420. // and adjust the working set count.
  421. //
  422. MiReleaseWsle (WorkingSetIndex, &CurrentProcess->Vm);
  423. if ((Locked.LockedInWs == 1) || (Locked.LockedInMemory == 1)) {
  424. //
  425. // This entry is locked.
  426. //
  427. ASSERT (WorkingSetIndex < MmWorkingSetList->FirstDynamic);
  428. MmWorkingSetList->FirstDynamic -= 1;
  429. if (WorkingSetIndex != MmWorkingSetList->FirstDynamic) {
  430. Entry = MmWorkingSetList->FirstDynamic;
  431. ASSERT (MmWsle[Entry].u1.e1.Valid);
  432. MiSwapWslEntries (Entry,
  433. WorkingSetIndex,
  434. &CurrentProcess->Vm,
  435. FALSE);
  436. }
  437. }
  438. else {
  439. ASSERT (WorkingSetIndex >= MmWorkingSetList->FirstDynamic);
  440. }
  441. }
  442. LastPteThisPage -= 1;
  443. TempVa = (PVOID)((ULONG_PTR)TempVa - PAGE_SIZE);
  444. } while (LastPteThisPage >= PointerPte);
  445. }
  446. do {
  447. TempPte = *PointerPte;
  448. if (TempPte.u.Long != 0) {
  449. //
  450. // One less used page table entry in this page table page.
  451. //
  452. MI_DECREMENT_USED_PTES_BY_HANDLE (UsedPageTableHandle);
  453. if (IS_PTE_NOT_DEMAND_ZERO (TempPte)) {
  454. if (LastProtoPte != NULL) {
  455. if (ProtoPte >= LastProtoPte) {
  456. ProtoPte = MiGetProtoPteAddress (Vad, MI_VA_TO_VPN(Va));
  457. Subsection = MiLocateSubsection (Vad, MI_VA_TO_VPN(Va));
  458. //
  459. // Subsection may be NULL if this PTE contains the
  460. // "search the VAD tree" encoding and the
  461. // corresponding prototype PTE is in the unused
  462. // PTE range of the segment - ie: a thread tried
  463. // to reach beyond the end of his section,
  464. // we encode the PTE this way initially during
  465. // the fault processing and then later during
  466. // the fault give the thread the AV - we don't
  467. // clear the PTE encoding then, so we have to
  468. // handle it now.
  469. //
  470. if (Subsection != NULL) {
  471. LastProtoPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
  472. }
  473. else {
  474. //
  475. // No more prototype PTEs need to be deleted
  476. // as we've passed the end of the valid portion
  477. // of the segment, so clear LastProtoPte.
  478. //
  479. LastProtoPte = NULL;
  480. }
  481. }
  482. #if DBG
  483. if ((Vad->u.VadFlags.ImageMap != 1) && (LastProtoPte != NULL)) {
  484. if ((ProtoPte < Subsection->SubsectionBase) ||
  485. (ProtoPte >= LastProtoPte)) {
  486. DbgPrint ("bad proto PTE %p va %p Vad %p sub %p\n",
  487. ProtoPte,Va,Vad,Subsection);
  488. DbgBreakPoint();
  489. }
  490. }
  491. #endif
  492. }
  493. if ((TempPte.u.Hard.Valid == 0) &&
  494. (TempPte.u.Soft.Prototype == 1) &&
  495. (AddressSpaceDeletion == TRUE)) {
  496. //
  497. // Pure (ie: not forked) prototype PTEs don't need PFN
  498. // protection to be deleted.
  499. //
  500. ASSERT (CurrentProcess->CloneRoot == NULL);
  501. #if DBG
  502. if ((PointerPte <= MiHighestUserPte) &&
  503. (TempPte.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) &&
  504. (ProtoPte != MiPteToProto (PointerPte))) {
  505. //
  506. // Make sure the prototype PTE is a fork
  507. // prototype PTE.
  508. //
  509. CloneBlock = (PMMCLONE_BLOCK)MiPteToProto (PointerPte);
  510. CloneDescriptor = MiLocateCloneAddress (CurrentProcess, (PVOID)CloneBlock);
  511. if (CloneDescriptor == NULL) {
  512. DbgPrint("0PrototypePte %p Clone desc %p\n",
  513. PrototypePte, CloneDescriptor);
  514. MiFormatPte (PointerPte);
  515. ASSERT (FALSE);
  516. }
  517. }
  518. #endif
  519. InvalidPtes += 1;
  520. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  521. }
  522. else {
  523. if (PfnHeld == FALSE) {
  524. PfnHeld = TRUE;
  525. LOCK_PFN (OldIrql);
  526. }
  527. Waited = MiDeletePte (PointerPte,
  528. (PVOID)Va,
  529. AddressSpaceDeletion,
  530. CurrentProcess,
  531. ProtoPte,
  532. &FlushList,
  533. OldIrql);
  534. #if (_MI_PAGING_LEVELS >= 3)
  535. //
  536. // This must be recalculated here if MiDeletePte
  537. // dropped the PFN lock (this can happen when
  538. // dealing with POSIX forked pages). Since the
  539. // used PTE count is kept in the PFN entry
  540. // which could have been paged out and replaced
  541. // during this window, recomputation of its
  542. // address (not the contents) is necessary.
  543. //
  544. if (Waited != 0) {
  545. MI_CHECK_USED_PTES_HANDLE (Va);
  546. UsedPageTableHandle = MI_GET_USED_PTES_HANDLE (Va);
  547. }
  548. #endif
  549. InvalidPtes = 0;
  550. }
  551. }
  552. else {
  553. InvalidPtes += 1;
  554. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  555. }
  556. }
  557. else {
  558. InvalidPtes += 1;
  559. }
  560. if (InvalidPtes == 16) {
  561. if (PfnHeld == TRUE) {
  562. if (FlushList.Count != 0) {
  563. MiFlushPteList (&FlushList, FALSE);
  564. }
  565. ASSERT (OldIrql != MM_NOIRQL);
  566. UNLOCK_PFN (OldIrql);
  567. PfnHeld = FALSE;
  568. }
  569. else {
  570. ASSERT (FlushList.Count == 0);
  571. }
  572. InvalidPtes = 0;
  573. }
  574. Va += PAGE_SIZE;
  575. PointerPte += 1;
  576. ProtoPte += 1;
  577. ASSERT64 (PointerPpe->u.Hard.Valid == 1);
  578. ASSERT (PointerPde->u.Hard.Valid == 1);
  579. //
  580. // If not at the end of a page table and still within the specified
  581. // range, then just march directly on to the next PTE.
  582. //
  583. }
  584. while ((!MiIsVirtualAddressOnPdeBoundary(Va)) && (Va <= EndingAddress));
  585. //
  586. // The virtual address is on a page directory boundary:
  587. //
  588. // 1. Flush the PTEs for the previous page table page.
  589. // 2. Delete the previous page directory & page table if appropriate.
  590. // 3. Attempt to leap forward skipping over empty page directories
  591. // and page tables where possible.
  592. //
  593. //
  594. // If all the entries have been eliminated from the previous
  595. // page table page, delete the page table page itself.
  596. //
  597. if (PfnHeld == TRUE) {
  598. if (FlushList.Count != 0) {
  599. MiFlushPteList (&FlushList, FALSE);
  600. }
  601. }
  602. else {
  603. ASSERT (FlushList.Count == 0);
  604. }
  605. //
  606. // If all the entries have been eliminated from the previous
  607. // page table page, delete the page table page itself.
  608. //
  609. ASSERT64 (PointerPpe->u.Hard.Valid == 1);
  610. ASSERT (PointerPde->u.Hard.Valid == 1);
  611. #if (_MI_PAGING_LEVELS >= 3)
  612. MI_CHECK_USED_PTES_HANDLE (PointerPte - 1);
  613. #endif
  614. if ((MI_GET_USED_PTES_FROM_HANDLE (UsedPageTableHandle) == 0) &&
  615. (PointerPde->u.Long != 0)) {
  616. if (PfnHeld == FALSE) {
  617. PfnHeld = TRUE;
  618. LOCK_PFN (OldIrql);
  619. }
  620. #if (_MI_PAGING_LEVELS >= 3)
  621. UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE (PointerPte - 1);
  622. MI_DECREMENT_USED_PTES_BY_HANDLE (UsedPageDirectoryHandle);
  623. #endif
  624. TempVa = MiGetVirtualAddressMappedByPte(PointerPde);
  625. MiDeletePte (PointerPde,
  626. TempVa,
  627. FALSE,
  628. CurrentProcess,
  629. NULL,
  630. NULL,
  631. OldIrql);
  632. #if (_MI_PAGING_LEVELS >= 3)
  633. if ((MI_GET_USED_PTES_FROM_HANDLE (UsedPageDirectoryHandle) == 0) &&
  634. (PointerPpe->u.Long != 0)) {
  635. #if (_MI_PAGING_LEVELS >= 4)
  636. UsedPageDirectoryHandle = MI_GET_USED_PTES_HANDLE (PointerPde);
  637. MI_DECREMENT_USED_PTES_BY_HANDLE (UsedPageDirectoryHandle);
  638. #endif
  639. TempVa = MiGetVirtualAddressMappedByPte(PointerPpe);
  640. MiDeletePte (PointerPpe,
  641. TempVa,
  642. FALSE,
  643. CurrentProcess,
  644. NULL,
  645. NULL,
  646. OldIrql);
  647. #if (_MI_PAGING_LEVELS >= 4)
  648. if ((MI_GET_USED_PTES_FROM_HANDLE (UsedPageDirectoryHandle) == 0) &&
  649. (PointerPxe->u.Long != 0)) {
  650. TempVa = MiGetVirtualAddressMappedByPte(PointerPxe);
  651. MiDeletePte (PointerPxe,
  652. TempVa,
  653. FALSE,
  654. CurrentProcess,
  655. NULL,
  656. NULL,
  657. OldIrql);
  658. }
  659. #endif
  660. }
  661. #endif
  662. ASSERT (OldIrql != MM_NOIRQL);
  663. UNLOCK_PFN (OldIrql);
  664. PfnHeld = FALSE;
  665. }
  666. if (PfnHeld == TRUE) {
  667. ASSERT (OldIrql != MM_NOIRQL);
  668. UNLOCK_PFN (OldIrql);
  669. PfnHeld = FALSE;
  670. }
  671. if (Va > EndingAddress) {
  672. //
  673. // All done, return.
  674. //
  675. return;
  676. }
  677. PointerPde = MiGetPdeAddress (Va);
  678. PointerPpe = MiGetPpeAddress (Va);
  679. PointerPxe = MiGetPxeAddress (Va);
  680. } while (TRUE);
  681. }
  682. ULONG
  683. MiDeletePte (
  684. IN PMMPTE PointerPte,
  685. IN PVOID VirtualAddress,
  686. IN ULONG AddressSpaceDeletion,
  687. IN PEPROCESS CurrentProcess,
  688. IN PMMPTE PrototypePte,
  689. IN PMMPTE_FLUSH_LIST PteFlushList OPTIONAL,
  690. IN KIRQL OldIrql
  691. )
  692. /*++
  693. Routine Description:
  694. This routine deletes the contents of the specified PTE. The PTE
  695. can be in one of the following states:
  696. - active and valid
  697. - transition
  698. - in paging file
  699. - in prototype PTE format
  700. Arguments:
  701. PointerPte - Supplies a pointer to the PTE to delete.
  702. VirtualAddress - Supplies the virtual address which corresponds to
  703. the PTE. This is used to locate the working set entry
  704. to eliminate it.
  705. AddressSpaceDeletion - Supplies TRUE if the address space is being
  706. deleted, FALSE otherwise. If TRUE is specified
  707. the TB is not flushed and valid addresses are
  708. not removed from the working set.
  709. CurrentProcess - Supplies a pointer to the current process.
  710. PrototypePte - Supplies a pointer to the prototype PTE which currently
  711. or originally mapped this page. This is used to determine
  712. if the PTE is a fork PTE and should have its reference block
  713. decremented.
  714. PteFlushList - Supplies a flush list to use if the TB flush can be
  715. deferred to the caller.
  716. OldIrql - Supplies the IRQL the caller acquired the PFN lock at.
  717. Return Value:
  718. Nonzero if this routine released mutexes and locks, FALSE if not.
  719. Environment:
  720. Kernel mode, APCs disabled, PFN lock and working set mutex held.
  721. --*/
  722. {
  723. PMMPTE PointerPde;
  724. PMMPFN Pfn1;
  725. PMMPFN Pfn2;
  726. MMPTE PteContents;
  727. WSLE_NUMBER WorkingSetIndex;
  728. WSLE_NUMBER Entry;
  729. MMWSLENTRY Locked;
  730. WSLE_NUMBER WsPfnIndex;
  731. PMMCLONE_BLOCK CloneBlock;
  732. PMMCLONE_DESCRIPTOR CloneDescriptor;
  733. ULONG DroppedLocks;
  734. PFN_NUMBER PageFrameIndex;
  735. PFN_NUMBER PageTableFrameIndex;
  736. MM_PFN_LOCK_ASSERT();
  737. DroppedLocks = 0;
  738. #if DBG
  739. if (MmDebug & MM_DBG_PTE_UPDATE) {
  740. DbgPrint("deleting PTE\n");
  741. MiFormatPte (PointerPte);
  742. }
  743. #endif
  744. PteContents = *PointerPte;
  745. if (PteContents.u.Hard.Valid == 1) {
  746. #ifdef _X86_
  747. #if DBG
  748. #if !defined(NT_UP)
  749. if (PteContents.u.Hard.Writable == 1) {
  750. ASSERT (PteContents.u.Hard.Dirty == 1);
  751. }
  752. #endif //NTUP
  753. #endif //DBG
  754. #endif //X86
  755. //
  756. // PTE is valid. Check PFN database to see if this is a prototype PTE.
  757. //
  758. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE(&PteContents);
  759. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  760. WsPfnIndex = Pfn1->u1.WsIndex;
  761. #if DBG
  762. if (MmDebug & MM_DBG_PTE_UPDATE) {
  763. MiFormatPfn(Pfn1);
  764. }
  765. #endif
  766. CloneDescriptor = NULL;
  767. if (Pfn1->u3.e1.PrototypePte == 1) {
  768. CloneBlock = (PMMCLONE_BLOCK)Pfn1->PteAddress;
  769. //
  770. // Capture the state of the modified bit for this PTE.
  771. //
  772. MI_CAPTURE_DIRTY_BIT_TO_PFN (PointerPte, Pfn1);
  773. //
  774. // Decrement the share and valid counts of the page table
  775. // page which maps this PTE.
  776. //
  777. PointerPde = MiGetPteAddress (PointerPte);
  778. if (PointerPde->u.Hard.Valid == 0) {
  779. #if (_MI_PAGING_LEVELS < 3)
  780. if (!NT_SUCCESS(MiCheckPdeForPagedPool (PointerPte))) {
  781. #endif
  782. KeBugCheckEx (MEMORY_MANAGEMENT,
  783. 0x61940,
  784. (ULONG_PTR) PointerPte,
  785. (ULONG_PTR) PointerPde->u.Long,
  786. (ULONG_PTR) VirtualAddress);
  787. #if (_MI_PAGING_LEVELS < 3)
  788. }
  789. #endif
  790. }
  791. PageTableFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE(PointerPde);
  792. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  793. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  794. //
  795. // Decrement the share count for the physical page.
  796. //
  797. MiDecrementShareCountInline (Pfn1, PageFrameIndex);
  798. //
  799. // Check to see if this is a fork prototype PTE and if so
  800. // update the clone descriptor address.
  801. //
  802. if (PointerPte <= MiHighestUserPte) {
  803. if (PrototypePte != Pfn1->PteAddress) {
  804. //
  805. // Locate the clone descriptor within the clone tree.
  806. //
  807. CloneDescriptor = MiLocateCloneAddress (CurrentProcess, (PVOID)CloneBlock);
  808. if (CloneDescriptor == NULL) {
  809. KeBugCheckEx (MEMORY_MANAGEMENT,
  810. 0x400,
  811. (ULONG_PTR) PointerPte,
  812. (ULONG_PTR) PrototypePte,
  813. (ULONG_PTR) Pfn1->PteAddress);
  814. }
  815. }
  816. }
  817. }
  818. else {
  819. if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte) {
  820. KeBugCheckEx (MEMORY_MANAGEMENT,
  821. 0x401,
  822. (ULONG_PTR) PointerPte,
  823. (ULONG_PTR) PointerPte->u.Long,
  824. (ULONG_PTR) Pfn1->PteAddress);
  825. }
  826. //
  827. // Initializing CloneBlock & PointerPde is not needed for
  828. // correctness but without it the compiler cannot compile this code
  829. // W4 to check for use of uninitialized variables.
  830. //
  831. CloneBlock = NULL;
  832. PointerPde = NULL;
  833. ASSERT (Pfn1->u2.ShareCount == 1);
  834. //
  835. // This PTE is NOT a prototype PTE, delete the physical page.
  836. //
  837. // Decrement the share and valid counts of the page table
  838. // page which maps this PTE.
  839. //
  840. MiDecrementShareCountInline (MI_PFN_ELEMENT(Pfn1->u4.PteFrame),
  841. Pfn1->u4.PteFrame);
  842. MI_SET_PFN_DELETED (Pfn1);
  843. //
  844. // Decrement the share count for the physical page. As the page
  845. // is private it will be put on the free list.
  846. //
  847. MiDecrementShareCount (Pfn1, PageFrameIndex);
  848. //
  849. // Decrement the count for the number of private pages.
  850. //
  851. CurrentProcess->NumberOfPrivatePages -= 1;
  852. }
  853. //
  854. // Find the WSLE for this page and eliminate it.
  855. //
  856. // If we are deleting the system portion of the address space, do
  857. // not remove WSLEs or flush translation buffers as there can be
  858. // no other usage of this address space.
  859. //
  860. if (AddressSpaceDeletion == FALSE) {
  861. WorkingSetIndex = MiLocateWsle (VirtualAddress,
  862. MmWorkingSetList,
  863. WsPfnIndex);
  864. ASSERT (WorkingSetIndex != WSLE_NULL_INDEX);
  865. //
  866. // Check to see if this entry is locked in the working set
  867. // or locked in memory.
  868. //
  869. Locked = MmWsle[WorkingSetIndex].u1.e1;
  870. MiRemoveWsle (WorkingSetIndex, MmWorkingSetList);
  871. //
  872. // Add this entry to the list of free working set entries
  873. // and adjust the working set count.
  874. //
  875. MiReleaseWsle (WorkingSetIndex, &CurrentProcess->Vm);
  876. if ((Locked.LockedInWs == 1) || (Locked.LockedInMemory == 1)) {
  877. //
  878. // This entry is locked.
  879. //
  880. ASSERT (WorkingSetIndex < MmWorkingSetList->FirstDynamic);
  881. MmWorkingSetList->FirstDynamic -= 1;
  882. if (WorkingSetIndex != MmWorkingSetList->FirstDynamic) {
  883. Entry = MmWorkingSetList->FirstDynamic;
  884. ASSERT (MmWsle[Entry].u1.e1.Valid);
  885. MiSwapWslEntries (Entry,
  886. WorkingSetIndex,
  887. &CurrentProcess->Vm,
  888. FALSE);
  889. }
  890. }
  891. else {
  892. ASSERT (WorkingSetIndex >= MmWorkingSetList->FirstDynamic);
  893. }
  894. }
  895. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  896. //
  897. // Flush the entry out of the TB.
  898. //
  899. if (!ARGUMENT_PRESENT (PteFlushList)) {
  900. KeFlushSingleTb (VirtualAddress, FALSE);
  901. }
  902. else {
  903. if (PteFlushList->Count != MM_MAXIMUM_FLUSH_COUNT) {
  904. PteFlushList->FlushVa[PteFlushList->Count] = VirtualAddress;
  905. PteFlushList->Count += 1;
  906. }
  907. }
  908. if (AddressSpaceDeletion == FALSE) {
  909. if (CloneDescriptor != NULL) {
  910. //
  911. // Flush TBs as this clone path could release the PFN lock.
  912. //
  913. if (ARGUMENT_PRESENT (PteFlushList)) {
  914. MiFlushPteList (PteFlushList, FALSE);
  915. }
  916. //
  917. // Decrement the reference count for the clone block,
  918. // note that this could release and reacquire
  919. // the mutexes hence cannot be done until after the
  920. // working set entry has been removed.
  921. //
  922. if (MiDecrementCloneBlockReference (CloneDescriptor,
  923. CloneBlock,
  924. CurrentProcess,
  925. OldIrql)) {
  926. //
  927. // The working set mutex was released, so the current
  928. // current page directory & table page may have been
  929. // paged out (note they cannot be deleted because the
  930. // address space mutex is always held throughout).
  931. //
  932. DroppedLocks = 1;
  933. //
  934. // Ensure the PDE (and any table above it) are still
  935. // resident.
  936. //
  937. MiMakePdeExistAndMakeValid (PointerPde,
  938. CurrentProcess,
  939. OldIrql);
  940. }
  941. }
  942. }
  943. }
  944. else if (PteContents.u.Soft.Prototype == 1) {
  945. //
  946. // This is a prototype PTE, if it is a fork PTE clean up the
  947. // fork structures.
  948. //
  949. if ((PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) &&
  950. (PointerPte <= MiHighestUserPte) &&
  951. (PrototypePte != MiPteToProto (PointerPte))) {
  952. CloneBlock = (PMMCLONE_BLOCK) MiPteToProto (PointerPte);
  953. CloneDescriptor = MiLocateCloneAddress (CurrentProcess,
  954. (PVOID) CloneBlock);
  955. if (CloneDescriptor == NULL) {
  956. KeBugCheckEx (MEMORY_MANAGEMENT,
  957. 0x403,
  958. (ULONG_PTR) PointerPte,
  959. (ULONG_PTR) PrototypePte,
  960. (ULONG_PTR) PteContents.u.Long);
  961. }
  962. //
  963. // Decrement the reference count for the clone block,
  964. // note that this could release and reacquire
  965. // the mutexes.
  966. //
  967. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  968. if (ARGUMENT_PRESENT (PteFlushList)) {
  969. MiFlushPteList (PteFlushList, FALSE);
  970. }
  971. if (MiDecrementCloneBlockReference (CloneDescriptor,
  972. CloneBlock,
  973. CurrentProcess,
  974. OldIrql)) {
  975. //
  976. // The working set mutex was released, so the current
  977. // current page directory & table page may have been
  978. // paged out (note they cannot be deleted because the
  979. // address space mutex is always held throughout).
  980. //
  981. DroppedLocks = 1;
  982. //
  983. // Ensure the PDE (and any table above it) are still
  984. // resident.
  985. //
  986. PointerPde = MiGetPteAddress (PointerPte);
  987. MiMakePdeExistAndMakeValid (PointerPde,
  988. CurrentProcess,
  989. OldIrql);
  990. }
  991. }
  992. }
  993. else if (PteContents.u.Soft.Transition == 1) {
  994. //
  995. // This is a transition PTE. (Page is private)
  996. //
  997. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  998. if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte) {
  999. KeBugCheckEx (MEMORY_MANAGEMENT,
  1000. 0x402,
  1001. (ULONG_PTR) PointerPte,
  1002. (ULONG_PTR) PointerPte->u.Long,
  1003. (ULONG_PTR) Pfn1->PteAddress);
  1004. }
  1005. MI_SET_PFN_DELETED (Pfn1);
  1006. PageTableFrameIndex = Pfn1->u4.PteFrame;
  1007. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  1008. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  1009. //
  1010. // Check the reference count for the page, if the reference
  1011. // count is zero, move the page to the free list, if the reference
  1012. // count is not zero, ignore this page. When the reference count
  1013. // goes to zero, it will be placed on the free list.
  1014. //
  1015. if (Pfn1->u3.e2.ReferenceCount == 0) {
  1016. MiUnlinkPageFromList (Pfn1);
  1017. MiReleasePageFileSpace (Pfn1->OriginalPte);
  1018. MiInsertPageInFreeList (MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE(&PteContents));
  1019. }
  1020. //
  1021. // Decrement the count for the number of private pages.
  1022. //
  1023. CurrentProcess->NumberOfPrivatePages -= 1;
  1024. }
  1025. else {
  1026. //
  1027. // Must be page file space.
  1028. //
  1029. if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) {
  1030. if (MiReleasePageFileSpace (PteContents)) {
  1031. //
  1032. // Decrement the count for the number of private pages.
  1033. //
  1034. CurrentProcess->NumberOfPrivatePages -= 1;
  1035. }
  1036. }
  1037. }
  1038. //
  1039. // Zero the PTE contents.
  1040. //
  1041. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  1042. return DroppedLocks;
  1043. }
  1044. VOID
  1045. MiDeleteValidSystemPte (
  1046. IN PMMPTE PointerPte,
  1047. IN PVOID VirtualAddress,
  1048. IN PMMSUPPORT WsInfo,
  1049. IN PMMPTE_FLUSH_LIST PteFlushList OPTIONAL
  1050. )
  1051. /*++
  1052. Routine Description:
  1053. This routine deletes the contents of the specified valid system or
  1054. session PTE. The PTE *MUST* be valid and private (ie: not a prototype).
  1055. Arguments:
  1056. PointerPte - Supplies a pointer to the PTE to delete.
  1057. VirtualAddress - Supplies the virtual address which corresponds to
  1058. the PTE. This is used to locate the working set entry
  1059. to eliminate it.
  1060. WsInfo - Supplies the working set structure to update.
  1061. PteFlushList - Supplies a flush list to use if the TB flush can be
  1062. deferred to the caller.
  1063. Return Value:
  1064. None.
  1065. Environment:
  1066. Kernel mode, APCs disabled, PFN lock and working set mutex held.
  1067. --*/
  1068. {
  1069. PMMPFN Pfn1;
  1070. MMPTE PteContents;
  1071. WSLE_NUMBER WorkingSetIndex;
  1072. WSLE_NUMBER Entry;
  1073. MMWSLENTRY Locked;
  1074. WSLE_NUMBER WsPfnIndex;
  1075. PFN_NUMBER PageFrameIndex;
  1076. PMMWSL WorkingSetList;
  1077. PMMWSLE Wsle;
  1078. MM_PFN_LOCK_ASSERT();
  1079. PteContents = *PointerPte;
  1080. ASSERT (PteContents.u.Hard.Valid == 1);
  1081. #ifdef _X86_
  1082. #if DBG
  1083. #if !defined(NT_UP)
  1084. if (PteContents.u.Hard.Writable == 1) {
  1085. ASSERT (PteContents.u.Hard.Dirty == 1);
  1086. }
  1087. #endif //NTUP
  1088. #endif //DBG
  1089. #endif //X86
  1090. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE(&PteContents);
  1091. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1092. WsPfnIndex = Pfn1->u1.WsIndex;
  1093. ASSERT (Pfn1->u3.e1.PrototypePte == 0);
  1094. if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte) {
  1095. KeBugCheckEx (MEMORY_MANAGEMENT,
  1096. 0x401,
  1097. (ULONG_PTR) PointerPte,
  1098. (ULONG_PTR) PointerPte->u.Long,
  1099. (ULONG_PTR) Pfn1->PteAddress);
  1100. }
  1101. ASSERT (Pfn1->u2.ShareCount == 1);
  1102. //
  1103. // This PTE is NOT a prototype PTE, delete the physical page.
  1104. //
  1105. // Decrement the share and valid counts of the page table
  1106. // page which maps this PTE.
  1107. //
  1108. MiDecrementShareCountInline (MI_PFN_ELEMENT(Pfn1->u4.PteFrame),
  1109. Pfn1->u4.PteFrame);
  1110. MI_SET_PFN_DELETED (Pfn1);
  1111. //
  1112. // Decrement the share count for the physical page. As the page
  1113. // is private it will be put on the free list.
  1114. //
  1115. MiDecrementShareCount (Pfn1, PageFrameIndex);
  1116. //
  1117. // Find the WSLE for this page and eliminate it.
  1118. //
  1119. // If we are deleting the system portion of the address space, do
  1120. // not remove WSLEs or flush translation buffers as there can be
  1121. // no other usage of this address space.
  1122. //
  1123. WorkingSetList = WsInfo->VmWorkingSetList;
  1124. WorkingSetIndex = MiLocateWsle (VirtualAddress,
  1125. WorkingSetList,
  1126. WsPfnIndex);
  1127. ASSERT (WorkingSetIndex != WSLE_NULL_INDEX);
  1128. //
  1129. // Check to see if this entry is locked in the working set
  1130. // or locked in memory.
  1131. //
  1132. Wsle = WorkingSetList->Wsle;
  1133. Locked = Wsle[WorkingSetIndex].u1.e1;
  1134. MiRemoveWsle (WorkingSetIndex, WorkingSetList);
  1135. //
  1136. // Add this entry to the list of free working set entries
  1137. // and adjust the working set count.
  1138. //
  1139. MiReleaseWsle (WorkingSetIndex, WsInfo);
  1140. if ((Locked.LockedInWs == 1) || (Locked.LockedInMemory == 1)) {
  1141. //
  1142. // This entry is locked.
  1143. //
  1144. ASSERT (WorkingSetIndex < WorkingSetList->FirstDynamic);
  1145. WorkingSetList->FirstDynamic -= 1;
  1146. if (WorkingSetIndex != WorkingSetList->FirstDynamic) {
  1147. Entry = WorkingSetList->FirstDynamic;
  1148. ASSERT (Wsle[Entry].u1.e1.Valid);
  1149. MiSwapWslEntries (Entry, WorkingSetIndex, WsInfo, FALSE);
  1150. }
  1151. }
  1152. else {
  1153. ASSERT (WorkingSetIndex >= WorkingSetList->FirstDynamic);
  1154. }
  1155. //
  1156. // Zero the PTE contents.
  1157. //
  1158. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  1159. //
  1160. // Flush the entry out of the TB.
  1161. //
  1162. if (!ARGUMENT_PRESENT (PteFlushList)) {
  1163. if (WsInfo == &MmSystemCacheWs) {
  1164. KeFlushSingleTb (VirtualAddress, TRUE);
  1165. }
  1166. else {
  1167. MI_FLUSH_SINGLE_SESSION_TB (VirtualAddress);
  1168. }
  1169. }
  1170. else {
  1171. if (PteFlushList->Count != MM_MAXIMUM_FLUSH_COUNT) {
  1172. PteFlushList->FlushVa[PteFlushList->Count] = VirtualAddress;
  1173. PteFlushList->Count += 1;
  1174. }
  1175. }
  1176. if (WsInfo->Flags.SessionSpace == 1) {
  1177. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_HASH_SHRINK, 1);
  1178. InterlockedExchangeAddSizeT (&MmSessionSpace->NonPagablePages, -1);
  1179. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_WS_HASHPAGE_FREE, 1);
  1180. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages, -1);
  1181. }
  1182. return;
  1183. }
  1184. ULONG
  1185. FASTCALL
  1186. MiReleasePageFileSpace (
  1187. IN MMPTE PteContents
  1188. )
  1189. /*++
  1190. Routine Description:
  1191. This routine frees the paging file allocated to the specified PTE.
  1192. Arguments:
  1193. PteContents - Supplies the PTE which is in page file format.
  1194. Return Value:
  1195. Returns TRUE if any paging file space was deallocated.
  1196. Environment:
  1197. Kernel mode, APCs disabled, PFN lock held.
  1198. --*/
  1199. {
  1200. ULONG FreeBit;
  1201. ULONG PageFileNumber;
  1202. PMMPAGING_FILE PageFile;
  1203. MM_PFN_LOCK_ASSERT();
  1204. if (PteContents.u.Soft.Prototype == 1) {
  1205. //
  1206. // Not in page file format.
  1207. //
  1208. return FALSE;
  1209. }
  1210. FreeBit = GET_PAGING_FILE_OFFSET (PteContents);
  1211. if ((FreeBit == 0) || (FreeBit == MI_PTE_LOOKUP_NEEDED)) {
  1212. //
  1213. // Page is not in a paging file, just return.
  1214. //
  1215. return FALSE;
  1216. }
  1217. PageFileNumber = GET_PAGING_FILE_NUMBER (PteContents);
  1218. ASSERT (RtlCheckBit( MmPagingFile[PageFileNumber]->Bitmap, FreeBit) == 1);
  1219. #if DBG
  1220. if ((FreeBit < 8192) && (PageFileNumber == 0)) {
  1221. ASSERT ((MmPagingFileDebug[FreeBit] & 1) != 0);
  1222. MmPagingFileDebug[FreeBit] ^= 1;
  1223. }
  1224. #endif
  1225. PageFile = MmPagingFile[PageFileNumber];
  1226. MI_CLEAR_BIT (PageFile->Bitmap->Buffer, FreeBit);
  1227. PageFile->FreeSpace += 1;
  1228. PageFile->CurrentUsage -= 1;
  1229. //
  1230. // Check to see if we should move some MDL entries for the
  1231. // modified page writer now that more free space is available.
  1232. //
  1233. if ((MmNumberOfActiveMdlEntries == 0) ||
  1234. (PageFile->FreeSpace == MM_USABLE_PAGES_FREE)) {
  1235. MiUpdateModifiedWriterMdls (PageFileNumber);
  1236. }
  1237. return TRUE;
  1238. }
  1239. VOID
  1240. FASTCALL
  1241. MiUpdateModifiedWriterMdls (
  1242. IN ULONG PageFileNumber
  1243. )
  1244. /*++
  1245. Routine Description:
  1246. This routine ensures the MDLs for the specified paging file
  1247. are in the proper state such that paging i/o can continue.
  1248. Arguments:
  1249. PageFileNumber - Supplies the page file number to check the MDLs for.
  1250. Return Value:
  1251. None.
  1252. Environment:
  1253. Kernel mode, PFN lock held.
  1254. --*/
  1255. {
  1256. ULONG i;
  1257. PMMMOD_WRITER_MDL_ENTRY WriterEntry;
  1258. //
  1259. // Put the MDL entries into the active list.
  1260. //
  1261. for (i = 0; i < MM_PAGING_FILE_MDLS; i += 1) {
  1262. if (MmPagingFile[PageFileNumber]->Entry[i]->CurrentList ==
  1263. &MmFreePagingSpaceLow) {
  1264. ASSERT (MmPagingFile[PageFileNumber]->Entry[i]->Links.Flink !=
  1265. MM_IO_IN_PROGRESS);
  1266. //
  1267. // Remove this entry and put it on the active list.
  1268. //
  1269. WriterEntry = MmPagingFile[PageFileNumber]->Entry[i];
  1270. RemoveEntryList (&WriterEntry->Links);
  1271. WriterEntry->CurrentList = &MmPagingFileHeader.ListHead;
  1272. KeSetEvent (&WriterEntry->PagingListHead->Event, 0, FALSE);
  1273. InsertTailList (&WriterEntry->PagingListHead->ListHead,
  1274. &WriterEntry->Links);
  1275. MmNumberOfActiveMdlEntries += 1;
  1276. }
  1277. }
  1278. return;
  1279. }
  1280. VOID
  1281. MiFlushPteList (
  1282. IN PMMPTE_FLUSH_LIST PteFlushList,
  1283. IN ULONG AllProcessors
  1284. )
  1285. /*++
  1286. Routine Description:
  1287. This routine flushes all the PTEs in the PTE flush list.
  1288. If the list has overflowed, the entire TB is flushed.
  1289. Arguments:
  1290. PteFlushList - Supplies a pointer to the list to be flushed.
  1291. AllProcessors - Supplies TRUE if the flush occurs on all processors.
  1292. Return Value:
  1293. None.
  1294. Environment:
  1295. Kernel mode, PFN or a pre-process AWE lock may optionally be held.
  1296. --*/
  1297. {
  1298. ULONG count;
  1299. ASSERT (ARGUMENT_PRESENT (PteFlushList));
  1300. count = PteFlushList->Count;
  1301. if (count != 0) {
  1302. if (count != 1) {
  1303. if (count < MM_MAXIMUM_FLUSH_COUNT) {
  1304. KeFlushMultipleTb (count,
  1305. &PteFlushList->FlushVa[0],
  1306. (BOOLEAN)AllProcessors);
  1307. }
  1308. else {
  1309. //
  1310. // Array has overflowed, flush the entire TB.
  1311. //
  1312. if (AllProcessors != FALSE) {
  1313. KeFlushEntireTb (TRUE, TRUE);
  1314. }
  1315. else {
  1316. KeFlushProcessTb (FALSE);
  1317. }
  1318. }
  1319. }
  1320. else {
  1321. KeFlushSingleTb (PteFlushList->FlushVa[0],
  1322. (BOOLEAN)AllProcessors);
  1323. }
  1324. PteFlushList->Count = 0;
  1325. }
  1326. return;
  1327. }