test: mmquota.c Source File

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

# mmquota.c

Go to the documentation of this file.

    00001 /*++
    00002
    00003 Copyright (c) 1989  Microsoft Corporation
    00004
    00005 Module Name:
    00006
    00007    mmquota.c
    00008
    00009 Abstract:
    00010
    00011     This module contains the routines which implement the quota and
    00012     commitment charging for memory management.
    00013
    00014 Author:
    00015
    00016     Lou Perazzoli (loup) 12-December-89
    00017     Landy Wang (landyw) 02-Jun-1997
    00018
    00019 Revision History:
    00020
    00021 --*/
    00022
    00023 #include "mi.h"
    00024
    00025 #define MM_MAXIMUM_QUOTA_OVERCHARGE 9
    00026
    00027 #define MM_DONT_EXTEND_SIZE 512
    00028
    00029 #define MM_COMMIT_POPUP_MAX ((512*1024)/PAGE_SIZE)
    00030
    00031 #define MM_EXTEND_COMMIT ((1024*1024)/PAGE_SIZE)
    00032
    00033 SIZE_T MmPeakCommitment;
    00034
    00035 SIZE_T MmExtendedCommit;
    00036
    00037 SIZE_T MmExtendedCommitLimit;
    00038
    00039 LOGICAL MiCommitExtensionActive = FALSE;
    00040
    00041 extern ULONG_PTR MmAllocatedPagedPool;
    00042
    00043 extern SIZE_T MmPageFileFullExtendPages;
    00044
    00045 ULONG MiOverCommitCallCount;
    00046 extern EPROCESS_QUOTA_BLOCK PspDefaultQuotaBlock;
    00047
    00048
    00049 LOGICAL
    00050 MiCauseOverCommitPopup(
    00051     IN SIZE_T NumberOfPages,
    00052     IN ULONG Extension
    00053     );
    00054
    00055
    00056 ULONG
    00057 FASTCALL
    00058 MiChargePageFileQuota (
    00059     IN SIZE_T QuotaCharge,
    00060     IN PEPROCESS CurrentProcess
    00061     )
    00062
    00063 /*++
    00064
    00065 Routine Description:
    00066
    00067     This routine checks to ensure the user has sufficient page file
    00068     quota remaining and, if so, charges the quota.  If not an exception
    00069     is raised.
    00070
    00071 Arguments:
    00072
    00073     QuotaCharge - Supplies the quota amount to charge.
    00074
    00075     CurrentProcess - Supplies a pointer to the current process.
    00076
    00077 Return Value:
    00078
    00079     TRUE if the quota was successfully charged, raises an exception
    00080     otherwise.
    00081
    00082 Environment:
    00083
    00084     Kernel mode, APCs disabled, WorkingSetLock and AddressCreation mutexes
    00085     held.
    00086
    00087 --*/
    00088
    00089 {
    00090     SIZE_T NewPagefileValue;
    00091     PEPROCESS_QUOTA_BLOCK QuotaBlock;
    00092     KIRQL OldIrql;
    00093
    00094     QuotaBlock = CurrentProcess->QuotaBlock;
    00095
    00096 retry_charge:
    00097     if ( QuotaBlock != &PspDefaultQuotaBlock) {
    00098         ExAcquireFastLock (&QuotaBlock->QuotaLock,&OldIrql);
    00099 do_charge:
    00100         NewPagefileValue = QuotaBlock->PagefileUsage + QuotaCharge;
    00101
    00102         if (NewPagefileValue > QuotaBlock->PagefileLimit) {
    00103             ExReleaseFastLock (&QuotaBlock->QuotaLock,OldIrql);
    00104             ExRaiseStatus (STATUS_PAGEFILE_QUOTA_EXCEEDED);
    00105         }
    00106
    00107         QuotaBlock->PagefileUsage = NewPagefileValue;
    00108
    00109         if (NewPagefileValue > QuotaBlock->PeakPagefileUsage) {
    00110             QuotaBlock->PeakPagefileUsage = NewPagefileValue;
    00111         }
    00112
    00113         NewPagefileValue = CurrentProcess->PagefileUsage + QuotaCharge;
    00114         CurrentProcess->PagefileUsage = NewPagefileValue;
    00115
    00116         if (NewPagefileValue > CurrentProcess->PeakPagefileUsage) {
    00117             CurrentProcess->PeakPagefileUsage = NewPagefileValue;
    00118         }
    00119         ExReleaseFastLock (&QuotaBlock->QuotaLock,OldIrql);
    00120     } else {
    00121         ExAcquireFastLock (&PspDefaultQuotaBlock.QuotaLock,&OldIrql);
    00122
    00123         if ( (QuotaBlock = CurrentProcess->QuotaBlock) != &PspDefaultQuotaBlock) {
    00124             ExReleaseFastLock(&PspDefaultQuotaBlock.QuotaLock,OldIrql);
    00125             goto retry_charge;
    00126         }
    00127         goto do_charge;
    00128     }
    00129     return TRUE;
    00130 }
    00131
    00132 VOID
    00133 MiReturnPageFileQuota (
    00134     IN SIZE_T QuotaCharge,
    00135     IN PEPROCESS CurrentProcess
    00136     )
    00137
    00138 /*++
    00139
    00140 Routine Description:
    00141
    00142     This routine releases page file quota.
    00143
    00144 Arguments:
    00145
    00146     QuotaCharge - Supplies the quota amount to charge.
    00147
    00148     CurrentProcess - Supplies a pointer to the current process.
    00149
    00150 Return Value:
    00151
    00152     none.
    00153
    00154 Environment:
    00155
    00156     Kernel mode, APCs disabled, WorkingSetLock and AddressCreation mutexes
    00157     held.
    00158
    00159 --*/
    00160
    00161 {
    00162
    00163     PEPROCESS_QUOTA_BLOCK QuotaBlock;
    00164     KIRQL OldIrql;
    00165
    00166     QuotaBlock = CurrentProcess->QuotaBlock;
    00167
    00168 retry_return:
    00169     if ( QuotaBlock != &PspDefaultQuotaBlock) {
    00170         ExAcquireFastLock (&QuotaBlock->QuotaLock, &OldIrql);
    00171 do_return:
    00172         ASSERT (CurrentProcess->PagefileUsage >= QuotaCharge);
    00173         CurrentProcess->PagefileUsage -= QuotaCharge;
    00174
    00175         ASSERT (QuotaBlock->PagefileUsage >= QuotaCharge);
    00176         QuotaBlock->PagefileUsage -= QuotaCharge;
    00177         ExReleaseFastLock(&QuotaBlock->QuotaLock,OldIrql);
    00178     } else {
    00179         ExAcquireFastLock (&PspDefaultQuotaBlock.QuotaLock, &OldIrql);
    00180         if ( (QuotaBlock = CurrentProcess->QuotaBlock) != &PspDefaultQuotaBlock ) {
    00181             ExReleaseFastLock(&PspDefaultQuotaBlock.QuotaLock,OldIrql);
    00182             goto retry_return;
    00183         }
    00184         goto do_return;
    00185     }
    00186     return;
    00187 }
    00188
    00189 LOGICAL
    00190 FASTCALL
    00191 MiChargeCommitment (
    00192     IN SIZE_T QuotaCharge,
    00193     IN PEPROCESS Process OPTIONAL
    00194     )
    00195
    00196 /*++
    00197
    00198 Routine Description:
    00199
    00200     This routine checks to ensure the system has sufficient page file
    00201     space remaining.
    00202
    00203 Arguments:
    00204
    00205     QuotaCharge - Supplies the quota amount to charge.
    00206
    00207     Process - Optionally supplies the current process IF AND ONLY IF
    00208               the working set mutex is held.  If the paging file
    00209               is being extended, the working set mutex is released if
    00210               this is non-null.
    00211
    00212 Return Value:
    00213
    00214     TRUE if there is sufficient space, FALSE if not.
    00215
    00216 Environment:
    00217
    00218     Kernel mode, APCs disabled, WorkingSetLock and AddressCreation mutexes
    00219     held.
    00220
    00221 --*/
    00222
    00223 {
    00224     KIRQL OldIrql;
    00225     SIZE_T NewCommitValue;
    00226     MMPAGE_FILE_EXPANSION PageExtend;
    00227     LOGICAL WsHeldSafe;
    00228
    00229 #if !defined (_WIN64)
    00230     ASSERT (QuotaCharge < 0x100000);
    00231 #endif
    00232
    00233     ExAcquireFastLock (&MmChargeCommitmentLock, &OldIrql);
    00234
    00235     NewCommitValue = MmTotalCommittedPages + QuotaCharge;
    00236
    00237     while (NewCommitValue > MmTotalCommitLimit) {
    00238
    00239         ExReleaseFastLock (&MmChargeCommitmentLock, OldIrql);
    00240
    00241         if (Process != NULL) {
    00242
    00243             //
    00244             // The working set lock may have been acquired safely or unsafely
    00245             // by our caller.  Handle both cases here and below.
    00246             //
    00247
    00248             UNLOCK_WS_REGARDLESS(Process, WsHeldSafe);
    00249         }
    00250
    00251         //
    00252         // Queue a message to the segment dereferencing / pagefile extending
    00253         // thread to see if the page file can be extended.  This is done
    00254         // in the context of a system thread due to mutexes which may
    00255         // currently be held.
    00256         //
    00257
    00258         PageExtend.RequestedExpansionSize = QuotaCharge;
    00259         PageExtend.Segment = NULL;
    00260         PageExtend.PageFileNumber = MI_EXTEND_ANY_PAGEFILE;
    00261         KeInitializeEvent (&PageExtend.Event, NotificationEvent, FALSE);
    00262
    00263         if (MiIssuePageExtendRequest (&PageExtend) == FALSE) {
    00264
    00265             if (Process != NULL) {
    00266                 LOCK_WS_REGARDLESS(Process, WsHeldSafe);
    00267             }
    00268
    00269             //
    00270             // If the quota is small enough, commit it anyway.  Otherwise
    00271             // return an error.
    00272             //
    00273
    00274             if (QuotaCharge < MM_MAXIMUM_QUOTA_OVERCHARGE) {
    00275
    00276                 //
    00277                 // Try the can't expand routine.
    00278                 //
    00279
    00280                 if (MiChargeCommitmentCantExpand (QuotaCharge, FALSE) == FALSE) {
    00281                     return FALSE;
    00282                 }
    00283             } else {
    00284
    00285                 //
    00286                 // Put up a popup and grant an extension if possible.
    00287                 //
    00288
    00289                 if (MiCauseOverCommitPopup (QuotaCharge, MM_EXTEND_COMMIT) == FALSE) {
    00290                     return FALSE;
    00291                 }
    00292             }
    00293             return TRUE;
    00294         }
    00295
    00296         if (Process != NULL) {
    00297             if (WsHeldSafe == TRUE) {
    00298                 LOCK_WS (Process);
    00299             }
    00300             else {
    00301                 LOCK_WS_UNSAFE (Process);
    00302             }
    00303         }
    00304
    00305         if (PageExtend.ActualExpansion == 0) {
    00306             if (MiCauseOverCommitPopup (QuotaCharge, MM_EXTEND_COMMIT) == FALSE) {
    00307                 return FALSE;
    00308             }
    00309             return TRUE;
    00310         }
    00311
    00312         ExAcquireFastLock (&MmChargeCommitmentLock, &OldIrql);
    00313         NewCommitValue = MmTotalCommittedPages + QuotaCharge;
    00314     }
    00315
    00316     MmTotalCommittedPages = NewCommitValue;
    00317     if ((MmTotalCommittedPages > MmPeakCommitment) &&
    00318         (MmPageFileFullExtendPages == 0)) {
    00319         MmPeakCommitment = MmTotalCommittedPages;
    00320     }
    00321
    00322     ExReleaseFastLock (&MmChargeCommitmentLock, OldIrql);
    00323
    00324     return TRUE;
    00325 }
    00326
    00327 LOGICAL
    00328 FASTCALL
    00329 MiChargeCommitmentCantExpand (
    00330     IN SIZE_T QuotaCharge,
    00331     IN ULONG MustSucceed
    00332     )
    00333
    00334 /*++
    00335
    00336 Routine Description:
    00337
    00338     This routine charges the specified commitment without attempting
    00339     to expand paging file and waiting for the expansion.  The routine
    00340     determines if the paging file space is exhausted, and if so,
    00341     it attempts to ascertain if the paging file space could be expanded.
    00342
    00343 Arguments:
    00344
    00345     QuotaCharge - Supplies the quota amount to charge.
    00346
    00347     MustSucceed - Supplies TRUE if the charge must succeed.
    00348
    00349 Return Value:
    00350
    00351     TRUE if the commitment was permitted, FALSE if not.
    00352
    00353 Environment:
    00354
    00355     Kernel mode, APCs disabled.
    00356
    00357 --*/
    00358
    00359 {
    00360     KIRQL OldIrql;
    00361     SIZE_T NewCommitValue;
    00362     SIZE_T ExtendAmount;
    00363
    00364     ExAcquireFastLock (&MmChargeCommitmentLock, &OldIrql);
    00365
    00366     //
    00367     // If the overcommitment is bigger than 512 pages, don't extend.
    00368     //
    00369
    00370     NewCommitValue = MmTotalCommittedPages + QuotaCharge;
    00371
    00372     if (!MustSucceed) {
    00373
    00374         if (NewCommitValue > MmTotalCommitLimit) {
    00375
    00376             if ((NewCommitValue - MmTotalCommitLimit > MM_DONT_EXTEND_SIZE) ||
    00377                 (NewCommitValue < MmTotalCommittedPages) ||
    00378                 (NewCommitValue > MmTotalCommitLimitMaximum)) {
    00379
    00380                     ExReleaseFastLock (&MmChargeCommitmentLock, OldIrql);
    00381                     return FALSE;
    00382             }
    00383         }
    00384         else if (NewCommitValue > MmTotalCommitLimitMaximum) {
    00385             ExReleaseFastLock (&MmChargeCommitmentLock, OldIrql);
    00386             return FALSE;
    00387         }
    00388     }
    00389
    00390     ExtendAmount = NewCommitValue - MmTotalCommitLimit;
    00391     MmTotalCommittedPages = NewCommitValue;
    00392
    00393     if (NewCommitValue > (MmTotalCommitLimit + 20)) {
    00394
    00395         //
    00396         // Attempt to expand the paging file, but don't wait
    00397         // to see if it succeeds.
    00398         //
    00399
    00400         if (MmAttemptForCantExtend.InProgress != FALSE) {
    00401
    00402             //
    00403             // An expansion request is already in progress, assume
    00404             // this will succeed.
    00405             //
    00406
    00407             ExReleaseFastLock (&MmChargeCommitmentLock, OldIrql);
    00408             return TRUE;
    00409         }
    00410
    00411         MmAttemptForCantExtend.InProgress = TRUE;
    00412         ExReleaseFastLock (&MmChargeCommitmentLock, OldIrql);
    00413
    00414         //
    00415         // Queue a message to the segment dereferencing / pagefile extending
    00416         // thread to see if the page file can be extended.  This is done
    00417         // in the context of a system thread due to mutexes which may
    00418         // currently be held.
    00419         //
    00420
    00421         if (QuotaCharge > ExtendAmount) {
    00422             ExtendAmount = QuotaCharge;
    00423         }
    00424
    00425         MmAttemptForCantExtend.RequestedExpansionSize = ExtendAmount;
    00426         ExAcquireFastLock (&MmDereferenceSegmentHeader.Lock, &OldIrql);
    00427         InsertTailList ( &MmDereferenceSegmentHeader.ListHead,
    00428                          &MmAttemptForCantExtend.DereferenceList);
    00429         ExReleaseFastLock (&MmDereferenceSegmentHeader.Lock, OldIrql);
    00430
    00431         KeReleaseSemaphore (&MmDereferenceSegmentHeader.Semaphore, 0L, 1L, FALSE);
    00432     }
    00433     else {
    00434         ExReleaseFastLock (&MmChargeCommitmentLock, OldIrql);
    00435     }
    00436
    00437     return TRUE;
    00438 }
    00439
    00440 VOID
    00441 FASTCALL
    00442 MiReturnCommitment (
    00443     IN SIZE_T QuotaCharge
    00444     )
    00445
    00446 /*++
    00447
    00448 Routine Description:
    00449
    00450     This routine releases page file quota.
    00451
    00452 Arguments:
    00453
    00454     QuotaCharge - Supplies the quota amount to charge.
    00455
    00456     CurrentProcess - Supplies a pointer to the current process.
    00457
    00458 Return Value:
    00459
    00460     none.
    00461
    00462 Environment:
    00463
    00464     Kernel mode, APCs disabled, WorkingSetLock and AddressCreation mutexes
    00465     held.
    00466
    00467 --*/
    00468
    00469 {
    00470     KIRQL OldIrql;
    00471
    00472     ExAcquireFastLock (&MmChargeCommitmentLock, &OldIrql);
    00473
    00474     ASSERT (MmTotalCommittedPages >= QuotaCharge);
    00475
    00476     MmTotalCommittedPages -= QuotaCharge;
    00477
    00478     //
    00479     // If commit allotments have been temporarily blocked then open the
    00480     // floodgates provided either enough commit has been freed or the pagefile
    00481     // extension has succeeded.
    00482     //
    00483
    00484     if (MmPageFileFullExtendPages) {
    00485         ASSERT (MmTotalCommittedPages >= MmPageFileFullExtendPages);
    00486         MmTotalCommittedPages -= MmPageFileFullExtendPages;
    00487         MmPageFileFullExtendPages = 0;
    00488     }
    00489
    00490     //
    00491     // If the system automatically granted an increase that can be returned
    00492     // now, do it.
    00493     //
    00494
    00495     if (MiCommitExtensionActive == TRUE) {
    00496
    00497         if ((MmExtendedCommitLimit != 0) &&
    00498             (MmTotalCommitLimit > MmTotalCommittedPages) &&
    00499             (MmTotalCommitLimit - MmTotalCommittedPages > MmExtendedCommitLimit)) {
    00500                 MmTotalCommitLimit -= MmExtendedCommitLimit;
    00501                 MmExtendedCommitLimit = 0;
    00502         }
    00503
    00504         if (MmExtendedCommit != 0) {
    00505             MmTotalCommittedPages -= MmExtendedCommit;
    00506             MmExtendedCommit = 0;
    00507         }
    00508
    00509         if ((MmExtendedCommitLimit == 0) && (MmExtendedCommit == 0)) {
    00510             MiCommitExtensionActive = FALSE;
    00511         }
    00512     }
    00513
    00514     ExReleaseFastLock (&MmChargeCommitmentLock, OldIrql);
    00515     return;
    00516 }
    00517
    00518 SIZE_T
    00519 MiCalculatePageCommitment (
    00520     IN PVOID StartingAddress,
    00521     IN PVOID EndingAddress,
    00522     IN PMMVAD Vad,
    00523     IN PEPROCESS Process
    00524     )
    00525
    00526 /*++
    00527
    00528 Routine Description:
    00529
    00530     This routine examines the range of pages from the starting address
    00531     up to and including the ending address and returns the commit charge
    00532     for  the pages within the range.
    00533
    00534 Arguments:
    00535
    00536     StartingAddress - Supplies the starting address of the range.
    00537
    00538     EndingAddress - Supplies the ending address of the range.
    00539
    00540     Vad - Supplies the virtual address descriptor which describes the range.
    00541
    00542     Process - Supplies the current process.
    00543
    00544 Return Value:
    00545
    00546     Commitment charge for the range.
    00547
    00548 Environment:
    00549
    00550     Kernel mode, APCs disabled, WorkingSetLock and AddressCreation mutexes
    00551     held.
    00552
    00553 --*/
    00554
    00555 {
    00556     PMMPTE PointerPte;
    00557     PMMPTE LastPte;
    00558     PMMPTE PointerPde;
    00559     PMMPTE PointerPpe;
    00560     PMMPTE TempEnd;
    00561     SIZE_T NumberOfCommittedPages;
    00562     ULONG Waited;
    00563
    00564     NumberOfCommittedPages = 0;
    00565
    00566     PointerPpe = MiGetPpeAddress (StartingAddress);
    00567     PointerPde = MiGetPdeAddress (StartingAddress);
    00568     PointerPte = MiGetPteAddress (StartingAddress);
    00569
    00570     if (Vad->u.VadFlags.MemCommit == 1) {
    00571
    00572         TempEnd = EndingAddress;
    00573
    00574         //
    00575         // All the pages are committed within this range.
    00576         //
    00577
    00578         NumberOfCommittedPages = BYTES_TO_PAGES ((PCHAR)TempEnd -
    00579                                                        (PCHAR)StartingAddress);
    00580
    00581
    00582         //
    00583         // Examine the PTEs to determine how many pages are committed.
    00584         //
    00585
    00586         LastPte = MiGetPteAddress (TempEnd);
    00587
    00588         do {
    00589
    00590             while (!MiDoesPpeExistAndMakeValid (PointerPpe,
    00591                                                 Process,
    00592                                                 FALSE,
    00593                                                 &Waited)) {
    00594
    00595                 //
    00596                 // No PPE exists for the starting address, therefore the page
    00597                 // is not committed.
    00598                 //
    00599
    00600                 PointerPpe += 1;
    00601                 PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
    00602                 PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    00603                 if (PointerPte > LastPte) {
    00604                     goto DoneCommit;
    00605                 }
    00606             }
    00607
    00608             Waited = 0;
    00609
    00610             while (!MiDoesPdeExistAndMakeValid (PointerPde,
    00611                                                 Process,
    00612                                                 FALSE,
    00613                                                 &Waited)) {
    00614
    00615                 //
    00616                 // No PDE exists for the starting address, therefore the page
    00617                 // is not committed.
    00618                 //
    00619
    00620                 PointerPde += 1;
    00621                 PointerPpe = MiGetPteAddress (PointerPde);
    00622                 PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    00623                 if (PointerPte > LastPte) {
    00624                     goto DoneCommit;
    00625                 }
    00626 #if defined (_WIN64)
    00627                 if (MiIsPteOnPdeBoundary (PointerPde)) {
    00628                     Waited = 1;
    00629                     break;
    00630                 }
    00631 #endif
    00632             }
    00633
    00634         } while (Waited != 0);
    00635
    00636 restart:
    00637
    00638         while (PointerPte <= LastPte) {
    00639
    00640             if (MiIsPteOnPdeBoundary (PointerPte)) {
    00641
    00642                 //
    00643                 // This is a PDE boundary, check to see if the entire
    00644                 // PPE/PDE pages exist.
    00645                 //
    00646
    00647                 PointerPde = MiGetPteAddress (PointerPte);
    00648                 PointerPpe = MiGetPteAddress (PointerPde);
    00649
    00650                 do {
    00651
    00652                     if (!MiDoesPpeExistAndMakeValid (PointerPpe,
    00653                                                      Process,
    00654                                                      FALSE,
    00655                                                      &Waited)) {
    00656
    00657                         //
    00658                         // No PDE exists for the starting address, check the VAD
    00659                         // to see if the pages are not committed.
    00660                         //
    00661
    00662                         PointerPpe += 1;
    00663                         PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
    00664                         PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    00665
    00666                         //
    00667                         // Check next page.
    00668                         //
    00669
    00670                         goto restart;
    00671                     }
    00672
    00673                     Waited = 0;
    00674
    00675                     if (!MiDoesPdeExistAndMakeValid (PointerPde,
    00676                                                      Process,
    00677                                                      FALSE,
    00678                                                      &Waited)) {
    00679
    00680                         //
    00681                         // No PDE exists for the starting address, check the VAD
    00682                         // to see if the pages are not committed.
    00683                         //
    00684
    00685                         PointerPde += 1;
    00686                         PointerPpe = MiGetPteAddress (PointerPde);
    00687                         PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    00688
    00689                         //
    00690                         // Check next page.
    00691                         //
    00692
    00693                         goto restart;
    00694                     }
    00695                 } while (Waited != 0);
    00696             }
    00697
    00698             //
    00699             // The PDE exists, examine the PTE.
    00700             //
    00701
    00702             if (PointerPte->u.Long != 0) {
    00703
    00704                 //
    00705                 // Has this page been explicitly decommitted?
    00706                 //
    00707
    00708                 if (MiIsPteDecommittedPage (PointerPte)) {
    00709
    00710                     //
    00711                     // This page is decommitted, remove it from the count.
    00712                     //
    00713
    00714                     NumberOfCommittedPages -= 1;
    00715
    00716                 }
    00717             }
    00718
    00719             PointerPte += 1;
    00720         }
    00721
    00722 DoneCommit:
    00723
    00724         if (TempEnd == EndingAddress) {
    00725             return NumberOfCommittedPages;
    00726         }
    00727
    00728     }
    00729
    00730     //
    00731     // Examine non committed range.
    00732     //
    00733
    00734     LastPte = MiGetPteAddress (EndingAddress);
    00735
    00736     do {
    00737
    00738         while (!MiDoesPpeExistAndMakeValid (PointerPpe,
    00739                                             Process,
    00740                                             FALSE,
    00741                                             &Waited)) {
    00742
    00743
    00744             //
    00745             // No PDE exists for the starting address, therefore the page
    00746             // is not committed.
    00747             //
    00748
    00749             PointerPpe += 1;
    00750             PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
    00751             PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    00752             if (PointerPte > LastPte) {
    00753                return NumberOfCommittedPages;
    00754             }
    00755         }
    00756
    00757         Waited = 0;
    00758
    00759         while (!MiDoesPdeExistAndMakeValid (PointerPde,
    00760                                             Process,
    00761                                             FALSE,
    00762                                             &Waited)) {
    00763
    00764             //
    00765             // No PDE exists for the starting address, therefore the page
    00766             // is not committed.
    00767             //
    00768
    00769             PointerPde += 1;
    00770             PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    00771             if (PointerPte > LastPte) {
    00772                return NumberOfCommittedPages;
    00773             }
    00774 #if defined (_WIN64)
    00775             if (MiIsPteOnPdeBoundary (PointerPde)) {
    00776                 PointerPpe = MiGetPteAddress (PointerPde);
    00777                 Waited = 1;
    00778                 break;
    00779             }
    00780 #endif
    00781         }
    00782
    00783     } while (Waited != 0);
    00784
    00785 restart2:
    00786
    00787     while (PointerPte <= LastPte) {
    00788
    00789         if (MiIsPteOnPdeBoundary (PointerPte)) {
    00790
    00791             //
    00792             // This is a PDE boundary, check to see if the entire
    00793             // PPE/PDE pages exist.
    00794             //
    00795
    00796             PointerPde = MiGetPteAddress (PointerPte);
    00797             PointerPpe = MiGetPteAddress (PointerPde);
    00798
    00799             do {
    00800
    00801                 if (!MiDoesPpeExistAndMakeValid (PointerPpe,
    00802                                                  Process,
    00803                                                  FALSE,
    00804                                                  &Waited)) {
    00805
    00806                     //
    00807                     // No PPE exists for the starting address, check the VAD
    00808                     // to see if the pages are not committed.
    00809                     //
    00810
    00811                     PointerPpe += 1;
    00812                     PointerPde = MiGetVirtualAddressMappedByPte (PointerPpe);
    00813                     PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    00814
    00815                     //
    00816                     // Check next page.
    00817                     //
    00818
    00819                     goto restart2;
    00820                 }
    00821
    00822                 Waited = 0;
    00823
    00824                 if (!MiDoesPdeExistAndMakeValid (PointerPde,
    00825                                                  Process,
    00826                                                  FALSE,
    00827                                                  &Waited)) {
    00828
    00829                     //
    00830                     // No PDE exists for the starting address, check the VAD
    00831                     // to see if the pages are not committed.
    00832                     //
    00833
    00834                     PointerPde += 1;
    00835                     PointerPpe = MiGetPteAddress (PointerPde);
    00836                     PointerPte = MiGetVirtualAddressMappedByPte (PointerPde);
    00837
    00838                     //
    00839                     // Check next page.
    00840                     //
    00841
    00842                     goto restart2;
    00843                 }
    00844
    00845             } while (Waited != 0);
    00846         }
    00847
    00848         //
    00849         // The PDE exists, examine the PTE.
    00850         //
    00851
    00852         if ((PointerPte->u.Long != 0) &&
    00853              (!MiIsPteDecommittedPage (PointerPte))) {
    00854
    00855             //
    00856             // This page is committed, count it.
    00857             //
    00858
    00859             NumberOfCommittedPages += 1;
    00860         }
    00861
    00862         PointerPte += 1;
    00863     }
    00864
    00865     return NumberOfCommittedPages;
    00866 }
    00867
    00868 VOID
    00869 MiReturnPageTablePageCommitment (
    00870     IN PVOID StartingAddress,
    00871     IN PVOID EndingAddress,
    00872     IN PEPROCESS CurrentProcess,
    00873     IN PMMVAD PreviousVad,
    00874     IN PMMVAD NextVad
    00875     )
    00876
    00877 /*++
    00878
    00879 Routine Description:
    00880
    00881     This routine returns commitment for COMPLETE page table pages which
    00882     span the virtual address range.  For example (assuming 4k pages),
    00883     if the StartingAddress =  64k and the EndingAddress = 5mb, no
    00884     page table charges would be freed as a complete page table page is
    00885     not covered by the range.  However, if the StartingAddress was 4mb
    00886     and the EndingAddress was 9mb, 1 page table page would be freed.
    00887
    00888 Arguments:
    00889
    00890     StartingAddress - Supplies the starting address of the range.
    00891
    00892     EndingAddress - Supplies the ending address of the range.
    00893
    00894     CurrentProcess - Supplies a pointer to the current process.
    00895
    00896     PreviousVad - Supplies a pointer to the previous VAD, NULL if none.
    00897
    00898     NextVad - Supplies a pointer to the next VAD, NULL if none.
    00899
    00900 Return Value:
    00901
    00902     None.
    00903
    00904 Environment:
    00905
    00906     Kernel mode, APCs disabled, WorkingSetLock and AddressCreation mutexes
    00907     held.
    00908
    00909 --*/
    00910
    00911 {
    00912 #ifdef _WIN64
    00913     DBG_UNREFERENCED_PARAMETER (StartingAddress);
    00914     DBG_UNREFERENCED_PARAMETER (EndingAddress);
    00915     DBG_UNREFERENCED_PARAMETER (CurrentProcess);
    00916     DBG_UNREFERENCED_PARAMETER (PreviousVad);
    00917     DBG_UNREFERENCED_PARAMETER (NextVad);
    00918 #else
    00919     ULONG NumberToClear;
    00920     LONG FirstPage;
    00921     LONG LastPage;
    00922     LONG PreviousPage;
    00923     LONG NextPage;
    00924
    00925     //
    00926     // Check to see if any page table pages would be freed.
    00927     //
    00928
    00929     ASSERT (StartingAddress != EndingAddress);
    00930
    00931     if (PreviousVad == NULL) {
    00932         PreviousPage = -1;
    00933     } else {
    00934         PreviousPage = MiGetPpePdeOffset (MI_VPN_TO_VA (PreviousVad->EndingVpn));
    00935     }
    00936
    00937     if (NextVad == NULL) {
    00938         NextPage = MiGetPpePdeOffset (MM_HIGHEST_USER_ADDRESS) + 1;
    00939     } else {
    00940         NextPage = MiGetPpePdeOffset (MI_VPN_TO_VA (NextVad->StartingVpn));
    00941     }
    00942
    00943     ASSERT (PreviousPage <= NextPage);
    00944
    00945     FirstPage = MiGetPpePdeOffset  (StartingAddress);
    00946
    00947     LastPage = MiGetPpePdeOffset (EndingAddress);
    00948
    00949     if (PreviousPage == FirstPage) {
    00950
    00951         //
    00952         // A VAD is within the starting page table page.
    00953         //
    00954
    00955         FirstPage += 1;
    00956     }
    00957
    00958     if (NextPage == LastPage) {
    00959
    00960         //
    00961         // A VAD is within the ending page table page.
    00962         //
    00963
    00964         LastPage -= 1;
    00965     }
    00966
    00967     //
    00968     // Indicate that the page table page is not in use.
    00969     //
    00970
    00971     if (FirstPage > LastPage) {
    00972         return;
    00973     }
    00974
    00975     NumberToClear = 1 + LastPage - FirstPage;
    00976
    00977     while (FirstPage <= LastPage) {
    00978         ASSERT (MI_CHECK_BIT (MmWorkingSetList->CommittedPageTables,
    00979                               FirstPage));
    00980
    00981         MI_CLEAR_BIT (MmWorkingSetList->CommittedPageTables, FirstPage);
    00982         FirstPage += 1;
    00983     }
    00984
    00985     MmWorkingSetList->NumberOfCommittedPageTables -= NumberToClear;
    00986     MiReturnCommitment (NumberToClear);
    00987     MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_PAGETABLES, NumberToClear);
    00988     MiReturnPageFileQuota (NumberToClear, CurrentProcess);
    00989
    00990     if (CurrentProcess->JobStatus & PS_JOB_STATUS_REPORT_COMMIT_CHANGES) {
    00991         PsChangeJobMemoryUsage(-(SSIZE_T)NumberToClear);
    00992     }
    00993     CurrentProcess->CommitCharge -= NumberToClear;
    00994
    00995     return;
    00996 #endif
    00997 }
    00998
    00999
    01000 LOGICAL
    01001 MiCauseOverCommitPopup(
    01002     SIZE_T NumberOfPages,
    01003     IN ULONG Extension
    01004     )
    01005
    01006 /*++
    01007
    01008 Routine Description:
    01009
    01010     This function causes an over commit popup to occur.  If a popup is pending
    01011     it returns FALSE.  Otherwise, it queues a popup to a noncritical worker
    01012     thread.
    01013
    01014 Arguments:
    01015
    01016     NumberOfPages - Supplies the number of pages of commit requested.
    01017
    01018     Extension - Supplies the extension to grant.
    01019
    01020 Return Value:
    01021
    01022     TRUE - An overcommit popup was queued.
    01023
    01024     FALSE - An overcommit popup is still pending and will not be queued.
    01025
    01026 --*/
    01027
    01028 {
    01029     KIRQL OldIrql;
    01030     BOOLEAN RaisedPopup;
    01031     ULONG PopupNumber;
    01032
    01033     if (NumberOfPages > MM_COMMIT_POPUP_MAX ||
    01034         MmTotalCommittedPages + NumberOfPages > MmTotalCommitLimitMaximum) {
    01035             return FALSE;
    01036     }
    01037
    01038     //
    01039     // Give the user a meaningful message - either to increase the minimum,
    01040     // maximum, or both.
    01041     //
    01042
    01043     if (MmTotalCommittedPages > MmTotalCommitLimitMaximum - 100) {
    01044         PopupNumber = STATUS_COMMITMENT_LIMIT;
    01045     }
    01046     else {
    01047         PopupNumber = STATUS_COMMITMENT_MINIMUM;
    01048     }
    01049
    01050     RaisedPopup = IoRaiseInformationalHardError (PopupNumber, NULL, NULL);
    01051
    01052     ExAcquireFastLock (&MmChargeCommitmentLock, &OldIrql);
    01053
    01054     if ((RaisedPopup == FALSE) && (MiOverCommitCallCount > 0)) {
    01055
    01056         //
    01057         // There is already a popup outstanding and we have not
    01058         // returned any of the quota.
    01059         //
    01060
    01061         ExReleaseFastLock (&MmChargeCommitmentLock, OldIrql);
    01062         return FALSE;
    01063     }
    01064
    01065     //
    01066     // Now that the commitment lock is held, ensure the commit is not being
    01067     // raised past the absolute maximum.
    01068     //
    01069
    01070     if (MmTotalCommittedPages + NumberOfPages > MmTotalCommitLimitMaximum) {
    01071         ExReleaseFastLock (&MmChargeCommitmentLock, OldIrql);
    01072         return FALSE;
    01073     }
    01074
    01075     //
    01076     // Don't automatically grant increases forever as this can allow the
    01077     // system to run out of available pages.
    01078     //
    01079
    01080     if (MmExtendedCommit > 1024) {
    01081         ExReleaseFastLock (&MmChargeCommitmentLock, OldIrql);
    01082         return FALSE;
    01083     }
    01084
    01085     MiOverCommitCallCount += 1;
    01086
    01087     MiCommitExtensionActive = TRUE;
    01088
    01089     MmTotalCommitLimit += Extension;
    01090     MmExtendedCommitLimit += Extension;
    01091
    01092     MmTotalCommittedPages += NumberOfPages;
    01093
    01094     //
    01095     // The caller will not release this commit, so we must earmark it now
    01096     // for later release.
    01097     //
    01098
    01099     if (Extension == 0) {
    01100         MmExtendedCommit += NumberOfPages;
    01101     }
    01102
    01103     if ((MmTotalCommittedPages > MmPeakCommitment) &&
    01104         (MmPageFileFullExtendPages == 0)) {
    01105         MmPeakCommitment = MmTotalCommittedPages;
    01106     }
    01107
    01108     ExReleaseFastLock (&MmChargeCommitmentLock, OldIrql);
    01109
    01110     return TRUE;
    01111 }
    01112
    01113
    01114 SIZE_T MmTotalPagedPoolQuota;
    01115 SIZE_T MmTotalNonPagedPoolQuota;
    01116
    01117 BOOLEAN
    01118 MmRaisePoolQuota(
    01119     IN POOL_TYPE PoolType,
    01120     IN SIZE_T OldQuotaLimit,
    01121     OUT PSIZE_T NewQuotaLimit
    01122     )
    01123
    01124 /*++
    01125
    01126 Routine Description:
    01127
    01128     This function is called (with a spinlock) whenever PS detects a quota
    01129     limit has been exceeded. The purpose of this function is to attempt to
    01130     increase the specified quota.
    01131
    01132 Arguments:
    01133
    01134     PoolType - Supplies the pool type of the quota to be raised
    01135
    01136     OldQuotaLimit - Supplies the current quota limit for this pool type
    01137
    01138     NewQuotaLimit - Returns the new limit
    01139
    01140 Return Value:
    01141
    01142     TRUE - The API succeeded and the quota limit was raised.
    01143
    01144     FALSE - We were unable to raise the quota limit.
    01145
    01146 Environment:
    01147
    01148     Kernel mode, QUOTA SPIN LOCK HELD!!
    01149
    01150 --*/
    01151
    01152 {
    01153     SIZE_T Limit;
    01154     PMM_PAGED_POOL_INFO PagedPoolInfo;
    01155
    01156     if (PoolType == PagedPool) {
    01157
    01158         //
    01159         // Check commit limit and make sure at least 1mb is available.
    01160         // Check to make sure 4mb of paged pool still exists.
    01161         //
    01162
    01163         PagedPoolInfo = &MmPagedPoolInfo;
    01164
    01165         if ((MmSizeOfPagedPoolInBytes >> PAGE_SHIFT) <
    01166             (PagedPoolInfo->AllocatedPagedPool + ((MMPAGED_QUOTA_CHECK) >> PAGE_SHIFT))) {
    01167
    01168             return FALSE;
    01169         }
    01170
    01171         MmTotalPagedPoolQuota += (MMPAGED_QUOTA_INCREASE);
    01172         *NewQuotaLimit = OldQuotaLimit + (MMPAGED_QUOTA_INCREASE);
    01173         return TRUE;
    01174
    01175     } else {
    01176
    01177         if ( (ULONG_PTR)(MmAllocatedNonPagedPool + ((1*1024*1024) >> PAGE_SHIFT)) < (MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT)) {
    01178             goto aok;
    01179             }
    01180
    01181         //
    01182         // Make sure 200 pages and 5mb of nonpaged pool expansion
    01183         // available.  Raise quota by 64k.
    01184         //
    01185
    01186         if ((MmAvailablePages < 200) ||
    01187             (MmResidentAvailablePages < ((MMNONPAGED_QUOTA_CHECK) >> PAGE_SHIFT))) {
    01188
    01189             return FALSE;
    01190         }
    01191
    01192         if (MmAvailablePages > ((4*1024*1024) >> PAGE_SHIFT)) {
    01193             Limit = (1*1024*1024) >> PAGE_SHIFT;
    01194         } else {
    01195             Limit = (4*1024*1024) >> PAGE_SHIFT;
    01196         }
    01197
    01198         if ((ULONG_PTR)((MmMaximumNonPagedPoolInBytes >> PAGE_SHIFT)) <
    01199             (MmAllocatedNonPagedPool + Limit)) {
    01200
    01201             return FALSE;
    01202         }
    01203 aok:
    01204         MmTotalNonPagedPoolQuota += (MMNONPAGED_QUOTA_INCREASE);
    01205         *NewQuotaLimit = OldQuotaLimit + (MMNONPAGED_QUOTA_INCREASE);
    01206         return TRUE;
    01207     }
    01208 }
    01209
    01210
    01211 VOID
    01212 MmReturnPoolQuota(
    01213     IN POOL_TYPE PoolType,
    01214     IN SIZE_T ReturnedQuota
    01215     )
    01216
    01217 /*++
    01218
    01219 Routine Description:
    01220
    01221     Returns pool quota.
    01222
    01223 Arguments:
    01224
    01225     PoolType - Supplies the pool type of the quota to be returned.
    01226
    01227     ReturnedQuota - Number of bytes returned.
    01228
    01229 Return Value:
    01230
    01231     NONE.
    01232
    01233 Environment:
    01234
    01235     Kernel mode, QUOTA SPIN LOCK HELD!!
    01236
    01237 --*/
    01238
    01239 {
    01240
    01241     if (PoolType == PagedPool) {
    01242         MmTotalPagedPoolQuota -= ReturnedQuota;
    01243     } else {
    01244         MmTotalNonPagedPoolQuota -= ReturnedQuota;
    01245     }
    01246
    01247     return;
    01248 }