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.

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