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.

1430 lines
37 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. mmquota.c
  5. Abstract:
  6. This module contains the routines which implement the quota and
  7. commitment charging for memory management.
  8. Author:
  9. Lou Perazzoli (loup) 12-December-89
  10. Landy Wang (landyw) 02-Jun-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. #define MM_MINIMAL_COMMIT_INCREASE 512
  15. SIZE_T MmPeakCommitment;
  16. LONG MiCommitPopups[2];
  17. ULONG MiChargeCommitmentFailures[2];
  18. extern ULONG_PTR MmAllocatedPagedPool;
  19. #ifdef ALLOC_PRAGMA
  20. #pragma alloc_text(INIT,MiInitializeCommitment)
  21. #pragma alloc_text(PAGE,MiCalculatePageCommitment)
  22. #pragma alloc_text(PAGE,MiReturnPageTablePageCommitment)
  23. #endif
  24. SIZE_T MmSystemCommitReserve = (5 * 1024 * 1024) / PAGE_SIZE;
  25. VOID
  26. MiInitializeCommitment (
  27. VOID
  28. )
  29. {
  30. if (MmNumberOfPhysicalPages < (33 * 1024 * 1024) / PAGE_SIZE) {
  31. MmSystemCommitReserve = (1 * 1024 * 1024) / PAGE_SIZE;
  32. }
  33. #if defined (_MI_DEBUG_COMMIT_LEAKS)
  34. MiCommitTraces = ExAllocatePoolWithTag (NonPagedPool,
  35. MI_COMMIT_TRACE_MAX * sizeof (MI_COMMIT_TRACES),
  36. 'tCmM');
  37. #endif
  38. }
  39. LOGICAL
  40. FASTCALL
  41. MiChargeCommitment (
  42. IN SIZE_T QuotaCharge,
  43. IN PEPROCESS Process OPTIONAL
  44. )
  45. /*++
  46. Routine Description:
  47. This routine checks to ensure the system has sufficient page file
  48. space remaining.
  49. Since this routine is generally used to charge commitment on behalf of
  50. usermode or other optional actions, this routine does not allow the
  51. caller to use up the very last morsels of commit on the premise that
  52. the operating system and drivers can put those to better use than any
  53. application in order to prevent the appearance of system hangs.
  54. Arguments:
  55. QuotaCharge - Supplies the quota amount to charge.
  56. Process - Optionally supplies the current process IF AND ONLY IF
  57. the working set mutex is held. If the paging file
  58. is being extended, the working set mutex is released if
  59. this is non-null.
  60. Return Value:
  61. TRUE if there is sufficient space, FALSE if not.
  62. Environment:
  63. Kernel mode, APCs disabled, WorkingSetLock and AddressCreation mutexes
  64. held.
  65. --*/
  66. {
  67. SIZE_T OldCommitValue;
  68. SIZE_T NewCommitValue;
  69. SIZE_T CommitLimit;
  70. MMPAGE_FILE_EXPANSION PageExtend;
  71. LOGICAL WsHeldSafe;
  72. ASSERT ((SSIZE_T)QuotaCharge > 0);
  73. #if DBG
  74. if (InitializationPhase > 1) {
  75. ULONG i;
  76. PKTHREAD Thread;
  77. Thread = KeGetCurrentThread ();
  78. for (i = 0; i < (ULONG)KeNumberProcessors; i += 1) {
  79. if (KiProcessorBlock[i]->IdleThread == Thread) {
  80. DbgPrint ("MMQUOTA: %x %p\n", i, Thread);
  81. DbgBreakPoint ();
  82. }
  83. }
  84. }
  85. #endif
  86. //
  87. // Initializing WsHeldSafe is not needed for correctness, but without it
  88. // the compiler cannot compile this code W4 to check for use of
  89. // uninitialized variables.
  90. //
  91. WsHeldSafe = FALSE;
  92. do {
  93. OldCommitValue = MmTotalCommittedPages;
  94. NewCommitValue = OldCommitValue + QuotaCharge;
  95. while (NewCommitValue + MmSystemCommitReserve > MmTotalCommitLimit) {
  96. //
  97. // If the pagefiles are already at the maximum, then don't
  98. // bother trying to extend them, but do trim the cache.
  99. //
  100. if (MmTotalCommitLimit + 100 >= MmTotalCommitLimitMaximum) {
  101. MiChargeCommitmentFailures[1] += 1;
  102. MiTrimSegmentCache ();
  103. if (MmTotalCommitLimit >= MmTotalCommitLimitMaximum) {
  104. MiCauseOverCommitPopup ();
  105. return FALSE;
  106. }
  107. }
  108. if (Process != NULL) {
  109. //
  110. // The working set lock may have been acquired safely or
  111. // unsafely by our caller. Handle both cases here and below.
  112. //
  113. UNLOCK_WS_REGARDLESS(Process, WsHeldSafe);
  114. }
  115. //
  116. // Queue a message to the segment dereferencing / pagefile extending
  117. // thread to see if the page file can be extended. This is done
  118. // in the context of a system thread due to mutexes which may
  119. // currently be held.
  120. //
  121. PageExtend.InProgress = 1;
  122. PageExtend.ActualExpansion = 0;
  123. PageExtend.RequestedExpansionSize = QuotaCharge;
  124. PageExtend.Segment = NULL;
  125. PageExtend.PageFileNumber = MI_EXTEND_ANY_PAGEFILE;
  126. KeInitializeEvent (&PageExtend.Event, NotificationEvent, FALSE);
  127. if ((MiIssuePageExtendRequest (&PageExtend) == FALSE) ||
  128. (PageExtend.ActualExpansion == 0)) {
  129. MiCauseOverCommitPopup ();
  130. MiChargeCommitmentFailures[0] += 1;
  131. if (Process != NULL) {
  132. LOCK_WS_REGARDLESS(Process, WsHeldSafe);
  133. }
  134. return FALSE;
  135. }
  136. if (Process != NULL) {
  137. LOCK_WS_REGARDLESS(Process, WsHeldSafe);
  138. }
  139. OldCommitValue = MmTotalCommittedPages;
  140. NewCommitValue = OldCommitValue + QuotaCharge;
  141. }
  142. #if defined(_WIN64)
  143. NewCommitValue = InterlockedCompareExchange64 (
  144. (PLONGLONG) &MmTotalCommittedPages,
  145. (LONGLONG) NewCommitValue,
  146. (LONGLONG) OldCommitValue);
  147. #else
  148. NewCommitValue = InterlockedCompareExchange (
  149. (PLONG) &MmTotalCommittedPages,
  150. (LONG) NewCommitValue,
  151. (LONG) OldCommitValue);
  152. #endif
  153. } while (NewCommitValue != OldCommitValue);
  154. //
  155. // Success.
  156. //
  157. MM_TRACK_COMMIT (MM_DBG_COMMIT_CHARGE_NORMAL, QuotaCharge);
  158. if (MmTotalCommittedPages > MmPeakCommitment) {
  159. MmPeakCommitment = MmTotalCommittedPages;
  160. }
  161. //
  162. // Success. If system commit exceeds 90%, attempt a preemptive pagefile
  163. // increase anyway.
  164. //
  165. NewCommitValue = MmTotalCommittedPages;
  166. CommitLimit = MmTotalCommitLimit;
  167. if (NewCommitValue > ((CommitLimit/10)*9)) {
  168. if (CommitLimit < MmTotalCommitLimitMaximum) {
  169. //
  170. // Attempt to expand the paging file, but don't wait
  171. // to see if it succeeds.
  172. //
  173. NewCommitValue = NewCommitValue - ((CommitLimit/100)*85);
  174. MiIssuePageExtendRequestNoWait (NewCommitValue);
  175. }
  176. else {
  177. //
  178. // If the pagefiles are already at the maximum, then don't
  179. // bother trying to extend them, but do trim the cache.
  180. //
  181. if (MmTotalCommitLimit + 100 >= MmTotalCommitLimitMaximum) {
  182. MiTrimSegmentCache ();
  183. }
  184. }
  185. }
  186. return TRUE;
  187. }
  188. LOGICAL
  189. FASTCALL
  190. MiChargeCommitmentCantExpand (
  191. IN SIZE_T QuotaCharge,
  192. IN ULONG MustSucceed
  193. )
  194. /*++
  195. Routine Description:
  196. This routine charges the specified commitment without attempting
  197. to expand paging files and waiting for the expansion. The routine
  198. determines if the paging file space is exhausted, and if so,
  199. it attempts to ascertain if the paging file space could be expanded.
  200. Arguments:
  201. QuotaCharge - Supplies the quota amount to charge.
  202. MustSucceed - Supplies TRUE if the charge must succeed.
  203. Return Value:
  204. TRUE if the commitment was permitted, FALSE if not.
  205. Environment:
  206. Kernel mode, APCs disabled.
  207. --*/
  208. {
  209. SIZE_T CommitLimit;
  210. SIZE_T ExtendAmount;
  211. SIZE_T OldCommitValue;
  212. SIZE_T NewCommitValue;
  213. ASSERT ((SSIZE_T)QuotaCharge > 0);
  214. ASSERT32 ((QuotaCharge < 0x100000) || (QuotaCharge < MmTotalCommitLimit));
  215. do {
  216. OldCommitValue = MmTotalCommittedPages;
  217. NewCommitValue = OldCommitValue + QuotaCharge;
  218. if ((NewCommitValue > MmTotalCommitLimit) && (!MustSucceed)) {
  219. if ((NewCommitValue < MmTotalCommittedPages) ||
  220. (MmTotalCommitLimit + 100 >= MmTotalCommitLimitMaximum)) {
  221. MiChargeCommitmentFailures[1] += 1;
  222. return FALSE;
  223. }
  224. //
  225. // Attempt to expand the paging file, but don't wait
  226. // to see if it succeeds.
  227. //
  228. MiChargeCommitmentFailures[0] += 1;
  229. MiIssuePageExtendRequestNoWait (MM_MINIMAL_COMMIT_INCREASE);
  230. return FALSE;
  231. }
  232. #if defined(_WIN64)
  233. NewCommitValue = InterlockedCompareExchange64 (
  234. (PLONGLONG) &MmTotalCommittedPages,
  235. (LONGLONG) NewCommitValue,
  236. (LONGLONG) OldCommitValue);
  237. #else
  238. NewCommitValue = InterlockedCompareExchange (
  239. (PLONG) &MmTotalCommittedPages,
  240. (LONG) NewCommitValue,
  241. (LONG) OldCommitValue);
  242. #endif
  243. } while (NewCommitValue != OldCommitValue);
  244. MM_TRACK_COMMIT (MM_DBG_COMMIT_CHARGE_CANT_EXPAND, QuotaCharge);
  245. //
  246. // Success. If system commit exceeds 90%, attempt a preemptive pagefile
  247. // increase anyway.
  248. //
  249. NewCommitValue = MmTotalCommittedPages;
  250. CommitLimit = MmTotalCommitLimit;
  251. if ((NewCommitValue > ((CommitLimit/10)*9)) &&
  252. (CommitLimit < MmTotalCommitLimitMaximum)) {
  253. //
  254. // Attempt to expand the paging file, but don't wait
  255. // to see if it succeeds.
  256. //
  257. // Queue a message to the segment dereferencing / pagefile extending
  258. // thread to see if the page file can be extended. This is done
  259. // in the context of a system thread due to mutexes which may
  260. // currently be held.
  261. //
  262. ExtendAmount = NewCommitValue - ((CommitLimit/100)*85);
  263. if (QuotaCharge > ExtendAmount) {
  264. ExtendAmount = QuotaCharge;
  265. }
  266. MiIssuePageExtendRequestNoWait (ExtendAmount);
  267. }
  268. return TRUE;
  269. }
  270. LOGICAL
  271. FASTCALL
  272. MiChargeTemporaryCommitmentForReduction (
  273. IN SIZE_T QuotaCharge
  274. )
  275. /*++
  276. Routine Description:
  277. This routine attempts to charge the specified commitment without
  278. expanding the paging file.
  279. This is typically called just prior to reducing the pagefile size.
  280. Arguments:
  281. QuotaCharge - Supplies the quota amount to charge.
  282. Return Value:
  283. TRUE if the commitment was permitted, FALSE if not.
  284. Environment:
  285. Kernel mode, APCs disabled.
  286. --*/
  287. {
  288. SIZE_T OldCommitValue;
  289. SIZE_T NewCommitValue;
  290. ASSERT ((SSIZE_T)QuotaCharge > 0);
  291. ASSERT32 (QuotaCharge < 0x100000);
  292. do {
  293. OldCommitValue = MmTotalCommittedPages;
  294. NewCommitValue = OldCommitValue + QuotaCharge;
  295. if (NewCommitValue > MmTotalCommitLimit) {
  296. return FALSE;
  297. }
  298. #if defined(_WIN64)
  299. NewCommitValue = InterlockedCompareExchange64 (
  300. (PLONGLONG) &MmTotalCommittedPages,
  301. (LONGLONG) NewCommitValue,
  302. (LONGLONG) OldCommitValue);
  303. #else
  304. NewCommitValue = InterlockedCompareExchange (
  305. (PLONG) &MmTotalCommittedPages,
  306. (LONG) NewCommitValue,
  307. (LONG) OldCommitValue);
  308. #endif
  309. } while (NewCommitValue != OldCommitValue);
  310. //
  311. // Success.
  312. //
  313. MM_TRACK_COMMIT (MM_DBG_COMMIT_CHARGE_NORMAL, QuotaCharge);
  314. if (MmTotalCommittedPages > MmPeakCommitment) {
  315. MmPeakCommitment = MmTotalCommittedPages;
  316. }
  317. return TRUE;
  318. }
  319. SIZE_T
  320. MiCalculatePageCommitment (
  321. IN PVOID StartingAddress,
  322. IN PVOID EndingAddress,
  323. IN PMMVAD Vad,
  324. IN PEPROCESS Process
  325. )
  326. /*++
  327. Routine Description:
  328. This routine examines the range of pages from the starting address
  329. up to and including the ending address and returns the commit charge
  330. for the pages within the range.
  331. Arguments:
  332. StartingAddress - Supplies the starting address of the range.
  333. EndingAddress - Supplies the ending address of the range.
  334. Vad - Supplies the virtual address descriptor which describes the range.
  335. Process - Supplies the current process.
  336. Return Value:
  337. Commitment charge for the range.
  338. Environment:
  339. Kernel mode, APCs disabled, WorkingSetLock and AddressCreation mutexes
  340. held.
  341. --*/
  342. {
  343. PMMPTE PointerPte;
  344. PMMPTE LastPte;
  345. PMMPTE PointerPde;
  346. PMMPTE PointerPpe;
  347. PMMPTE PointerPxe;
  348. SIZE_T NumberOfCommittedPages;
  349. ULONG Waited;
  350. PointerPxe = MiGetPxeAddress (StartingAddress);
  351. PointerPpe = MiGetPpeAddress (StartingAddress);
  352. PointerPde = MiGetPdeAddress (StartingAddress);
  353. PointerPte = MiGetPteAddress (StartingAddress);
  354. LastPte = MiGetPteAddress (EndingAddress);
  355. if (Vad->u.VadFlags.MemCommit == 1) {
  356. //
  357. // All the pages are committed within this range.
  358. //
  359. NumberOfCommittedPages = BYTES_TO_PAGES ((PCHAR)EndingAddress -
  360. (PCHAR)StartingAddress);
  361. //
  362. // Examine the PTEs to determine how many pages are committed.
  363. //
  364. do {
  365. #if (_MI_PAGING_LEVELS >= 4)
  366. retry:
  367. #endif
  368. while (!MiDoesPxeExistAndMakeValid (PointerPxe,
  369. Process,
  370. FALSE,
  371. &Waited)) {
  372. //
  373. // No PXE exists for the starting address, therefore the page
  374. // is not committed.
  375. //
  376. PointerPxe += 1;
  377. PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
  378. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  379. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  380. if (PointerPte > LastPte) {
  381. return NumberOfCommittedPages;
  382. }
  383. }
  384. #if (_MI_PAGING_LEVELS >= 4)
  385. Waited = 0;
  386. #endif
  387. while (!MiDoesPpeExistAndMakeValid (PointerPpe,
  388. Process,
  389. FALSE,
  390. &Waited)) {
  391. //
  392. // No PPE exists for the starting address, therefore the page
  393. // is not committed.
  394. //
  395. PointerPpe += 1;
  396. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  397. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  398. if (PointerPte > LastPte) {
  399. return NumberOfCommittedPages;
  400. }
  401. #if (_MI_PAGING_LEVELS >= 4)
  402. if (MiIsPteOnPdeBoundary (PointerPpe)) {
  403. PointerPxe = MiGetPteAddress (PointerPpe);
  404. goto retry;
  405. }
  406. #endif
  407. }
  408. #if (_MI_PAGING_LEVELS < 4)
  409. Waited = 0;
  410. #endif
  411. while (!MiDoesPdeExistAndMakeValid (PointerPde,
  412. Process,
  413. FALSE,
  414. &Waited)) {
  415. //
  416. // No PDE exists for the starting address, therefore the page
  417. // is not committed.
  418. //
  419. PointerPde += 1;
  420. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  421. if (PointerPte > LastPte) {
  422. return NumberOfCommittedPages;
  423. }
  424. #if (_MI_PAGING_LEVELS >= 3)
  425. if (MiIsPteOnPdeBoundary (PointerPde)) {
  426. PointerPpe = MiGetPteAddress (PointerPde);
  427. PointerPxe = MiGetPdeAddress (PointerPde);
  428. Waited = 1;
  429. break;
  430. }
  431. #endif
  432. }
  433. } while (Waited != 0);
  434. restart:
  435. while (PointerPte <= LastPte) {
  436. if (MiIsPteOnPdeBoundary (PointerPte)) {
  437. //
  438. // This is a PDE boundary, check to see if the all the
  439. // PXE/PPE/PDE pages exist.
  440. //
  441. PointerPde = MiGetPteAddress (PointerPte);
  442. PointerPpe = MiGetPteAddress (PointerPde);
  443. PointerPxe = MiGetPteAddress (PointerPpe);
  444. do {
  445. if (!MiDoesPxeExistAndMakeValid (PointerPxe,
  446. Process,
  447. FALSE,
  448. &Waited)) {
  449. //
  450. // No PDE exists for the starting address, check the VAD
  451. // to see if the pages are not committed.
  452. //
  453. PointerPxe += 1;
  454. PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
  455. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  456. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  457. //
  458. // Check next page.
  459. //
  460. goto restart;
  461. }
  462. #if (_MI_PAGING_LEVELS >= 4)
  463. Waited = 0;
  464. #endif
  465. if (!MiDoesPpeExistAndMakeValid (PointerPpe,
  466. Process,
  467. FALSE,
  468. &Waited)) {
  469. //
  470. // No PDE exists for the starting address, check the VAD
  471. // to see if the pages are not committed.
  472. //
  473. PointerPpe += 1;
  474. PointerPxe = MiGetPteAddress (PointerPpe);
  475. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  476. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  477. //
  478. // Check next page.
  479. //
  480. goto restart;
  481. }
  482. #if (_MI_PAGING_LEVELS < 4)
  483. Waited = 0;
  484. #endif
  485. if (!MiDoesPdeExistAndMakeValid (PointerPde,
  486. Process,
  487. FALSE,
  488. &Waited)) {
  489. //
  490. // No PDE exists for the starting address, check the VAD
  491. // to see if the pages are not committed.
  492. //
  493. PointerPde += 1;
  494. PointerPpe = MiGetPteAddress (PointerPde);
  495. PointerPxe = MiGetPteAddress (PointerPpe);
  496. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  497. //
  498. // Check next page.
  499. //
  500. goto restart;
  501. }
  502. } while (Waited != 0);
  503. }
  504. //
  505. // The PDE exists, examine the PTE.
  506. //
  507. if (PointerPte->u.Long != 0) {
  508. //
  509. // Has this page been explicitly decommitted?
  510. //
  511. if (MiIsPteDecommittedPage (PointerPte)) {
  512. //
  513. // This page is decommitted, remove it from the count.
  514. //
  515. NumberOfCommittedPages -= 1;
  516. }
  517. }
  518. PointerPte += 1;
  519. }
  520. return NumberOfCommittedPages;
  521. }
  522. //
  523. // Examine non committed range.
  524. //
  525. NumberOfCommittedPages = 0;
  526. do {
  527. #if (_MI_PAGING_LEVELS >= 4)
  528. retry2:
  529. #endif
  530. while (!MiDoesPxeExistAndMakeValid (PointerPxe,
  531. Process,
  532. FALSE,
  533. &Waited)) {
  534. //
  535. // No PXE exists for the starting address, therefore the page
  536. // is not committed.
  537. //
  538. PointerPxe += 1;
  539. PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
  540. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  541. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  542. if (PointerPte > LastPte) {
  543. return NumberOfCommittedPages;
  544. }
  545. }
  546. #if (_MI_PAGING_LEVELS >= 4)
  547. Waited = 0;
  548. #endif
  549. while (!MiDoesPpeExistAndMakeValid (PointerPpe,
  550. Process,
  551. FALSE,
  552. &Waited)) {
  553. //
  554. // No PPE exists for the starting address, therefore the page
  555. // is not committed.
  556. //
  557. PointerPpe += 1;
  558. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  559. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  560. if (PointerPte > LastPte) {
  561. return NumberOfCommittedPages;
  562. }
  563. #if (_MI_PAGING_LEVELS >= 4)
  564. if (MiIsPteOnPdeBoundary (PointerPpe)) {
  565. PointerPxe = MiGetPteAddress (PointerPpe);
  566. goto retry2;
  567. }
  568. #endif
  569. }
  570. #if (_MI_PAGING_LEVELS < 4)
  571. Waited = 0;
  572. #endif
  573. while (!MiDoesPdeExistAndMakeValid (PointerPde,
  574. Process,
  575. FALSE,
  576. &Waited)) {
  577. //
  578. // No PDE exists for the starting address, therefore the page
  579. // is not committed.
  580. //
  581. PointerPde += 1;
  582. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  583. if (PointerPte > LastPte) {
  584. return NumberOfCommittedPages;
  585. }
  586. #if (_MI_PAGING_LEVELS >= 3)
  587. if (MiIsPteOnPdeBoundary (PointerPde)) {
  588. PointerPpe = MiGetPteAddress (PointerPde);
  589. PointerPxe = MiGetPdeAddress (PointerPde);
  590. Waited = 1;
  591. break;
  592. }
  593. #endif
  594. }
  595. } while (Waited != 0);
  596. restart2:
  597. while (PointerPte <= LastPte) {
  598. if (MiIsPteOnPdeBoundary (PointerPte)) {
  599. //
  600. // This is a PDE boundary, check to see if the entire
  601. // PXE/PPE/PDE pages exist.
  602. //
  603. PointerPde = MiGetPteAddress (PointerPte);
  604. PointerPpe = MiGetPteAddress (PointerPde);
  605. PointerPxe = MiGetPdeAddress (PointerPde);
  606. do {
  607. if (!MiDoesPxeExistAndMakeValid (PointerPxe,
  608. Process,
  609. FALSE,
  610. &Waited)) {
  611. //
  612. // No PXE exists for the starting address, check the VAD
  613. // to see if the pages are not committed.
  614. //
  615. PointerPxe += 1;
  616. PointerPpe = MiGetVirtualAddressMappedByPte (PointerPxe);
  617. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  618. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  619. //
  620. // Check next page.
  621. //
  622. goto restart2;
  623. }
  624. #if (_MI_PAGING_LEVELS >= 4)
  625. Waited = 0;
  626. #endif
  627. if (!MiDoesPpeExistAndMakeValid (PointerPpe,
  628. Process,
  629. FALSE,
  630. &Waited)) {
  631. //
  632. // No PPE exists for the starting address, check the VAD
  633. // to see if the pages are not committed.
  634. //
  635. PointerPpe += 1;
  636. PointerPxe = MiGetPteAddress (PointerPpe);
  637. PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
  638. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  639. //
  640. // Check next page.
  641. //
  642. goto restart2;
  643. }
  644. #if (_MI_PAGING_LEVELS < 4)
  645. Waited = 0;
  646. #endif
  647. if (!MiDoesPdeExistAndMakeValid (PointerPde,
  648. Process,
  649. FALSE,
  650. &Waited)) {
  651. //
  652. // No PDE exists for the starting address, check the VAD
  653. // to see if the pages are not committed.
  654. //
  655. PointerPde += 1;
  656. PointerPpe = MiGetPteAddress (PointerPde);
  657. PointerPxe = MiGetPteAddress (PointerPpe);
  658. PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
  659. //
  660. // Check next page.
  661. //
  662. goto restart2;
  663. }
  664. } while (Waited != 0);
  665. }
  666. //
  667. // The PDE exists, examine the PTE.
  668. //
  669. if ((PointerPte->u.Long != 0) &&
  670. (!MiIsPteDecommittedPage (PointerPte))) {
  671. //
  672. // This page is committed, count it.
  673. //
  674. NumberOfCommittedPages += 1;
  675. }
  676. PointerPte += 1;
  677. }
  678. return NumberOfCommittedPages;
  679. }
  680. VOID
  681. MiReturnPageTablePageCommitment (
  682. IN PVOID StartingAddress,
  683. IN PVOID EndingAddress,
  684. IN PEPROCESS CurrentProcess,
  685. IN PMMVAD PreviousVad,
  686. IN PMMVAD NextVad
  687. )
  688. /*++
  689. Routine Description:
  690. This routine returns commitment for COMPLETE page table pages which
  691. span the virtual address range. For example (assuming 4k pages),
  692. if the StartingAddress = 64k and the EndingAddress = 5mb, no
  693. page table charges would be freed as a complete page table page is
  694. not covered by the range. However, if the StartingAddress was 4mb
  695. and the EndingAddress was 9mb, 1 page table page would be freed.
  696. Arguments:
  697. StartingAddress - Supplies the starting address of the range.
  698. EndingAddress - Supplies the ending address of the range.
  699. CurrentProcess - Supplies a pointer to the current process.
  700. PreviousVad - Supplies a pointer to the previous VAD, NULL if none.
  701. NextVad - Supplies a pointer to the next VAD, NULL if none.
  702. Return Value:
  703. None.
  704. Environment:
  705. Kernel mode, APCs disabled, WorkingSetLock and AddressCreation mutexes
  706. held.
  707. --*/
  708. {
  709. RTL_BITMAP VadBitMap;
  710. ULONG NumberToClear;
  711. ULONG StartBit;
  712. ULONG EndBit;
  713. LONG FirstPage;
  714. LONG LastPage;
  715. LONG PreviousPage;
  716. LONG NextPage;
  717. #if (_MI_PAGING_LEVELS >= 3)
  718. LONG FirstPdPage;
  719. LONG LastPdPage;
  720. LONG PreviousPdPage;
  721. LONG NextPdPage;
  722. #endif
  723. #if (_MI_PAGING_LEVELS >= 4)
  724. LONG FirstPpPage;
  725. LONG LastPpPage;
  726. LONG PreviousPpPage;
  727. LONG NextPpPage;
  728. #endif
  729. //
  730. // Check to see if any page table pages would be freed.
  731. //
  732. ASSERT (StartingAddress != EndingAddress);
  733. StartBit = (ULONG) (((ULONG_PTR) MI_64K_ALIGN (StartingAddress)) / X64K);
  734. EndBit = (ULONG) (((ULONG_PTR) MI_64K_ALIGN (EndingAddress)) / X64K);
  735. if (PreviousVad == NULL) {
  736. PreviousPage = -1;
  737. #if (_MI_PAGING_LEVELS >= 3)
  738. PreviousPdPage = -1;
  739. #endif
  740. #if (_MI_PAGING_LEVELS >= 4)
  741. PreviousPpPage = -1;
  742. #endif
  743. }
  744. else {
  745. PreviousPage = MiGetPdeIndex (MI_VPN_TO_VA (PreviousVad->EndingVpn));
  746. #if (_MI_PAGING_LEVELS >= 3)
  747. PreviousPdPage = MiGetPpeIndex (MI_VPN_TO_VA (PreviousVad->EndingVpn));
  748. #endif
  749. #if (_MI_PAGING_LEVELS >= 4)
  750. PreviousPpPage = MiGetPxeIndex (MI_VPN_TO_VA (PreviousVad->EndingVpn));
  751. #endif
  752. if (MI_64K_ALIGN (MI_VPN_TO_VA (PreviousVad->EndingVpn)) ==
  753. MI_64K_ALIGN (StartingAddress)) {
  754. StartBit += 1;
  755. }
  756. }
  757. if (NextVad == NULL) {
  758. NextPage = MiGetPdeIndex (MM_HIGHEST_USER_ADDRESS) + 1;
  759. #if (_MI_PAGING_LEVELS >= 3)
  760. NextPdPage = MiGetPpeIndex (MM_HIGHEST_USER_ADDRESS) + 1;
  761. #endif
  762. #if (_MI_PAGING_LEVELS >= 4)
  763. NextPpPage = MiGetPxeIndex (MM_HIGHEST_USER_ADDRESS) + 1;
  764. #endif
  765. }
  766. else {
  767. NextPage = MiGetPdeIndex (MI_VPN_TO_VA (NextVad->StartingVpn));
  768. #if (_MI_PAGING_LEVELS >= 3)
  769. NextPdPage = MiGetPpeIndex (MI_VPN_TO_VA (NextVad->StartingVpn));
  770. #endif
  771. #if (_MI_PAGING_LEVELS >= 4)
  772. NextPpPage = MiGetPxeIndex (MI_VPN_TO_VA (NextVad->StartingVpn));
  773. #endif
  774. if (MI_64K_ALIGN (MI_VPN_TO_VA (NextVad->StartingVpn)) ==
  775. MI_64K_ALIGN (EndingAddress)) {
  776. EndBit -= 1;
  777. }
  778. }
  779. ASSERT (PreviousPage <= NextPage);
  780. ASSERT64 (PreviousPdPage <= NextPdPage);
  781. #if (_MI_PAGING_LEVELS >= 4)
  782. ASSERT64 (PreviousPpPage <= NextPpPage);
  783. #endif
  784. FirstPage = MiGetPdeIndex (StartingAddress);
  785. LastPage = MiGetPdeIndex (EndingAddress);
  786. if (PreviousPage == FirstPage) {
  787. //
  788. // A VAD is within the starting page table page.
  789. //
  790. FirstPage += 1;
  791. }
  792. if (NextPage == LastPage) {
  793. //
  794. // A VAD is within the ending page table page.
  795. //
  796. LastPage -= 1;
  797. }
  798. if (StartBit <= EndBit) {
  799. //
  800. // Initialize the bitmap inline for speed.
  801. //
  802. VadBitMap.SizeOfBitMap = MiLastVadBit + 1;
  803. VadBitMap.Buffer = VAD_BITMAP_SPACE;
  804. #if defined (_WIN64) || defined (_X86PAE_)
  805. //
  806. // Only the first (PAGE_SIZE*8*64K) of VA space on NT64 is bitmapped.
  807. //
  808. if (EndBit > MiLastVadBit) {
  809. EndBit = MiLastVadBit;
  810. }
  811. if (StartBit <= MiLastVadBit) {
  812. RtlClearBits (&VadBitMap, StartBit, EndBit - StartBit + 1);
  813. if (MmWorkingSetList->VadBitMapHint > StartBit) {
  814. MmWorkingSetList->VadBitMapHint = StartBit;
  815. }
  816. }
  817. #else
  818. RtlClearBits (&VadBitMap, StartBit, EndBit - StartBit + 1);
  819. if (MmWorkingSetList->VadBitMapHint > StartBit) {
  820. MmWorkingSetList->VadBitMapHint = StartBit;
  821. }
  822. #endif
  823. }
  824. //
  825. // Indicate that the page table page is not in use.
  826. //
  827. if (FirstPage > LastPage) {
  828. return;
  829. }
  830. NumberToClear = 1 + LastPage - FirstPage;
  831. while (FirstPage <= LastPage) {
  832. ASSERT (MI_CHECK_BIT (MmWorkingSetList->CommittedPageTables,
  833. FirstPage));
  834. MI_CLEAR_BIT (MmWorkingSetList->CommittedPageTables, FirstPage);
  835. FirstPage += 1;
  836. }
  837. MmWorkingSetList->NumberOfCommittedPageTables -= NumberToClear;
  838. #if (_MI_PAGING_LEVELS >= 4)
  839. //
  840. // Return page directory parent charges here.
  841. //
  842. FirstPpPage = MiGetPxeIndex (StartingAddress);
  843. LastPpPage = MiGetPxeIndex (EndingAddress);
  844. if (PreviousPpPage == FirstPpPage) {
  845. //
  846. // A VAD is within the starting page directory parent page.
  847. //
  848. FirstPpPage += 1;
  849. }
  850. if (NextPpPage == LastPpPage) {
  851. //
  852. // A VAD is within the ending page directory parent page.
  853. //
  854. LastPpPage -= 1;
  855. }
  856. //
  857. // Indicate that the page directory page parent is not in use.
  858. //
  859. if (FirstPpPage <= LastPpPage) {
  860. MmWorkingSetList->NumberOfCommittedPageDirectoryParents -= (1 + LastPpPage - FirstPpPage);
  861. NumberToClear += (1 + LastPpPage - FirstPpPage);
  862. while (FirstPpPage <= LastPpPage) {
  863. ASSERT (MI_CHECK_BIT (MmWorkingSetList->CommittedPageDirectoryParents,
  864. FirstPpPage));
  865. MI_CLEAR_BIT (MmWorkingSetList->CommittedPageDirectoryParents, FirstPpPage);
  866. FirstPpPage += 1;
  867. }
  868. }
  869. #endif
  870. #if (_MI_PAGING_LEVELS >= 3)
  871. //
  872. // Return page directory charges here.
  873. //
  874. FirstPdPage = MiGetPpeIndex (StartingAddress);
  875. LastPdPage = MiGetPpeIndex (EndingAddress);
  876. if (PreviousPdPage == FirstPdPage) {
  877. //
  878. // A VAD is within the starting page directory page.
  879. //
  880. FirstPdPage += 1;
  881. }
  882. if (NextPdPage == LastPdPage) {
  883. //
  884. // A VAD is within the ending page directory page.
  885. //
  886. LastPdPage -= 1;
  887. }
  888. //
  889. // Indicate that the page directory page is not in use.
  890. //
  891. if (FirstPdPage <= LastPdPage) {
  892. MmWorkingSetList->NumberOfCommittedPageDirectories -= (1 + LastPdPage - FirstPdPage);
  893. NumberToClear += (1 + LastPdPage - FirstPdPage);
  894. while (FirstPdPage <= LastPdPage) {
  895. ASSERT (MI_CHECK_BIT (MmWorkingSetList->CommittedPageDirectories,
  896. FirstPdPage));
  897. MI_CLEAR_BIT (MmWorkingSetList->CommittedPageDirectories, FirstPdPage);
  898. FirstPdPage += 1;
  899. }
  900. }
  901. #endif
  902. MiReturnCommitment (NumberToClear);
  903. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_PAGETABLES, NumberToClear);
  904. PsReturnProcessPageFileQuota (CurrentProcess, NumberToClear);
  905. if (CurrentProcess->JobStatus & PS_JOB_STATUS_REPORT_COMMIT_CHANGES) {
  906. PsChangeJobMemoryUsage(-(SSIZE_T)NumberToClear);
  907. }
  908. CurrentProcess->CommitCharge -= NumberToClear;
  909. MI_INCREMENT_TOTAL_PROCESS_COMMIT (0 - NumberToClear);
  910. return;
  911. }
  912. VOID
  913. MiCauseOverCommitPopup (
  914. VOID
  915. )
  916. /*++
  917. Routine Description:
  918. This function causes an over commit popup to occur (if the popup has never
  919. been sent before).
  920. Arguments:
  921. None.
  922. Return Value:
  923. None.
  924. --*/
  925. {
  926. LONG PopupNumber;
  927. //
  928. // Give the user a meaningful message - either to increase the minimum,
  929. // maximum, or both.
  930. //
  931. if (MmTotalCommittedPages > MmTotalCommitLimitMaximum - 100) {
  932. if (InterlockedIncrement (&MiCommitPopups[0]) > 1) {
  933. InterlockedDecrement (&MiCommitPopups[0]);
  934. return;
  935. }
  936. PopupNumber = STATUS_COMMITMENT_LIMIT;
  937. }
  938. else {
  939. if (InterlockedIncrement (&MiCommitPopups[1]) > 1) {
  940. InterlockedDecrement (&MiCommitPopups[1]);
  941. return;
  942. }
  943. PopupNumber = STATUS_COMMITMENT_MINIMUM;
  944. }
  945. IoRaiseInformationalHardError (PopupNumber, NULL, NULL);
  946. }
  947. SIZE_T MmTotalPagedPoolQuota;
  948. SIZE_T MmTotalNonPagedPoolQuota;
  949. BOOLEAN
  950. MmRaisePoolQuota(
  951. IN POOL_TYPE PoolType,
  952. IN SIZE_T OldQuotaLimit,
  953. OUT PSIZE_T NewQuotaLimit
  954. )
  955. /*++
  956. Routine Description:
  957. This function is called (with a spinlock) whenever PS detects a quota
  958. limit has been exceeded. The purpose of this function is to attempt to
  959. increase the specified quota.
  960. Arguments:
  961. PoolType - Supplies the pool type of the quota to be raised
  962. OldQuotaLimit - Supplies the current quota limit for this pool type
  963. NewQuotaLimit - Returns the new limit
  964. Return Value:
  965. TRUE - The API succeeded and the quota limit was raised.
  966. FALSE - We were unable to raise the quota limit.
  967. Environment:
  968. Kernel mode, QUOTA SPIN LOCK HELD!!
  969. --*/
  970. {
  971. SIZE_T Limit;
  972. PMM_PAGED_POOL_INFO PagedPoolInfo;
  973. if (PoolType == PagedPool) {
  974. //
  975. // Check commit limit and make sure at least 1mb is available.
  976. // Check to make sure 4mb of paged pool still exists.
  977. //
  978. PagedPoolInfo = &MmPagedPoolInfo;
  979. if ((MmSizeOfPagedPoolInBytes >> PAGE_SHIFT) <
  980. (PagedPoolInfo->AllocatedPagedPool + ((MMPAGED_QUOTA_CHECK) >> PAGE_SHIFT))) {
  981. return FALSE;
  982. }
  983. MmTotalPagedPoolQuota += (MMPAGED_QUOTA_INCREASE);
  984. *NewQuotaLimit = OldQuotaLimit + (MMPAGED_QUOTA_INCREASE);
  985. return TRUE;
  986. } else {
  987. if ( (ULONG_PTR)(MmAllocatedNonPagedPool + ((1*1024*1024) >> PAGE_SHIFT)) < (MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT)) {
  988. goto aok;
  989. }
  990. //
  991. // Make sure 200 pages and 5mb of nonpaged pool expansion
  992. // available. Raise quota by 64k.
  993. //
  994. if ((MmAvailablePages < 200) ||
  995. (MmResidentAvailablePages < ((MMNONPAGED_QUOTA_CHECK) >> PAGE_SHIFT))) {
  996. return FALSE;
  997. }
  998. if (MmAvailablePages > ((4*1024*1024) >> PAGE_SHIFT)) {
  999. Limit = (1*1024*1024) >> PAGE_SHIFT;
  1000. } else {
  1001. Limit = (4*1024*1024) >> PAGE_SHIFT;
  1002. }
  1003. if ((ULONG_PTR)((MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT)) <
  1004. (MmAllocatedNonPagedPool + Limit)) {
  1005. return FALSE;
  1006. }
  1007. aok:
  1008. MmTotalNonPagedPoolQuota += (MMNONPAGED_QUOTA_INCREASE);
  1009. *NewQuotaLimit = OldQuotaLimit + (MMNONPAGED_QUOTA_INCREASE);
  1010. return TRUE;
  1011. }
  1012. }
  1013. VOID
  1014. MmReturnPoolQuota(
  1015. IN POOL_TYPE PoolType,
  1016. IN SIZE_T ReturnedQuota
  1017. )
  1018. /*++
  1019. Routine Description:
  1020. Returns pool quota.
  1021. Arguments:
  1022. PoolType - Supplies the pool type of the quota to be returned.
  1023. ReturnedQuota - Number of bytes returned.
  1024. Return Value:
  1025. NONE.
  1026. Environment:
  1027. Kernel mode, QUOTA SPIN LOCK HELD!!
  1028. --*/
  1029. {
  1030. if (PoolType == PagedPool) {
  1031. MmTotalPagedPoolQuota -= ReturnedQuota;
  1032. } else {
  1033. MmTotalNonPagedPoolQuota -= ReturnedQuota;
  1034. }
  1035. return;
  1036. }