518 lines
14 KiB
C
518 lines
14 KiB
C
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
psquota.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the quota mechanism for NT
|
|
|
|
Author:
|
|
|
|
Mark Lucovsky (markl) 18-Sep-1989
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "psp.h"
|
|
|
|
PEPROCESS_QUOTA_BLOCK
|
|
PsChargeSharedPoolQuota(
|
|
IN PEPROCESS Process,
|
|
IN SIZE_T PagedAmount,
|
|
IN SIZE_T NonPagedAmount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function charges shared pool quota of the specified pool type
|
|
to the specified process's pooled quota block. If the quota charge
|
|
would exceed the limits allowed to the process, then an exception is
|
|
raised and quota is not charged.
|
|
|
|
Arguments:
|
|
|
|
Process - Supplies the process to charge quota to.
|
|
|
|
PagedAmount - Supplies the amount of paged pool quota to charge.
|
|
|
|
PagedAmount - Supplies the amount of non paged pool quota to charge.
|
|
|
|
Return Value:
|
|
|
|
NULL - Quota was exceeded
|
|
|
|
NON-NULL - A referenced pointer to the quota block that was charged
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KIRQL OldIrql;
|
|
SIZE_T NewPoolUsage;
|
|
PEPROCESS_QUOTA_BLOCK QuotaBlock;
|
|
SIZE_T NewLimit;
|
|
|
|
ASSERT((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
|
|
|
|
QuotaBlock = Process->QuotaBlock;
|
|
|
|
retry_charge:
|
|
if ( QuotaBlock != &PspDefaultQuotaBlock ) {
|
|
ExAcquireSpinLock(&QuotaBlock->QuotaLock,&OldIrql);
|
|
do_charge:
|
|
|
|
if ( PagedAmount ) {
|
|
|
|
NewPoolUsage = QuotaBlock->QuotaPoolUsage[PagedPool] + PagedAmount;
|
|
|
|
|
|
// See if enough quota exists in the block to satisfy the
|
|
// request
|
|
|
|
|
|
if ( NewPoolUsage > QuotaBlock->QuotaPoolLimit[PagedPool] ) {
|
|
while ( (PspDefaultPagedLimit == 0) && MmRaisePoolQuota(PagedPool,QuotaBlock->QuotaPoolLimit[PagedPool],&NewLimit) ) {
|
|
QuotaBlock->QuotaPoolLimit[PagedPool] = NewLimit;
|
|
if ( NewPoolUsage <= NewLimit ) {
|
|
goto LimitRaised0;
|
|
}
|
|
}
|
|
//DbgPrint("PS: ChargeShared(0) Failed P %8x QB %8x PA %8x NPA %8x\n",Process,QuotaBlock,PagedAmount,NonPagedAmount);DbgBreakPoint();
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
return NULL;
|
|
}
|
|
LimitRaised0:
|
|
if ( NewPoolUsage < QuotaBlock->QuotaPoolUsage[PagedPool] ||
|
|
NewPoolUsage < PagedAmount ) {
|
|
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
//DbgPrint("PS: ChargeShared(1) Failed P %8x QB %8x PA %8x NPA %8x\n",Process,QuotaBlock,PagedAmount,NonPagedAmount);DbgBreakPoint();
|
|
return NULL;
|
|
}
|
|
|
|
QuotaBlock->QuotaPoolUsage[PagedPool] = NewPoolUsage;
|
|
if ( NewPoolUsage > QuotaBlock->QuotaPeakPoolUsage[PagedPool] ) {
|
|
QuotaBlock->QuotaPeakPoolUsage[PagedPool] = NewPoolUsage;
|
|
}
|
|
}
|
|
|
|
if ( NonPagedAmount ) {
|
|
|
|
NewPoolUsage = QuotaBlock->QuotaPoolUsage[NonPagedPool] + NonPagedAmount;
|
|
|
|
|
|
// See if enough quota exists in the block to satisfy the
|
|
// request
|
|
|
|
|
|
if ( NewPoolUsage > QuotaBlock->QuotaPoolLimit[NonPagedPool] ) {
|
|
while ( (PspDefaultNonPagedLimit == 0) && MmRaisePoolQuota(NonPagedPool,QuotaBlock->QuotaPoolLimit[NonPagedPool],&NewLimit) ) {
|
|
QuotaBlock->QuotaPoolLimit[NonPagedPool] = NewLimit;
|
|
if ( NewPoolUsage <= NewLimit ) {
|
|
goto LimitRaised1;
|
|
}
|
|
}
|
|
if ( PagedAmount ) {
|
|
QuotaBlock->QuotaPoolUsage[PagedPool] -= PagedAmount;
|
|
}
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
//DbgPrint("PS: ChargeShared(2) Failed P %8x QB %8x PA %8x NPA %8x\n",Process,QuotaBlock,PagedAmount,NonPagedAmount);DbgBreakPoint();
|
|
return NULL;
|
|
}
|
|
|
|
LimitRaised1:
|
|
if ( NewPoolUsage < QuotaBlock->QuotaPoolUsage[NonPagedPool] ||
|
|
NewPoolUsage < NonPagedAmount ) {
|
|
if ( PagedAmount ) {
|
|
QuotaBlock->QuotaPoolUsage[PagedPool] -= PagedAmount;
|
|
}
|
|
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
//DbgPrint("PS: ChargeShared(3) Failed P %8x QB %8x PA %8x NPA %8x\n",Process,QuotaBlock,PagedAmount,NonPagedAmount);DbgBreakPoint();
|
|
return NULL;
|
|
}
|
|
|
|
QuotaBlock->QuotaPoolUsage[NonPagedPool] = NewPoolUsage;
|
|
if ( NewPoolUsage > QuotaBlock->QuotaPeakPoolUsage[NonPagedPool] ) {
|
|
QuotaBlock->QuotaPeakPoolUsage[NonPagedPool] = NewPoolUsage;
|
|
}
|
|
}
|
|
|
|
QuotaBlock->ReferenceCount++;
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
}
|
|
else {
|
|
|
|
|
|
// Don't do ANY quota operations on the initial system process
|
|
|
|
|
|
if ( Process == PsInitialSystemProcess ) {
|
|
return (PEPROCESS_QUOTA_BLOCK)1;
|
|
}
|
|
|
|
ExAcquireSpinLock(&PspDefaultQuotaBlock.QuotaLock,&OldIrql);
|
|
if ( (QuotaBlock = Process->QuotaBlock) != &PspDefaultQuotaBlock ) {
|
|
ExReleaseSpinLock(&PspDefaultQuotaBlock.QuotaLock,OldIrql);
|
|
goto retry_charge;
|
|
}
|
|
goto do_charge;
|
|
|
|
}
|
|
return QuotaBlock;
|
|
|
|
}
|
|
|
|
VOID
|
|
PsReturnSharedPoolQuota(
|
|
IN PEPROCESS_QUOTA_BLOCK QuotaBlock,
|
|
IN SIZE_T PagedAmount,
|
|
IN SIZE_T NonPagedAmount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns pool quota of the specified pool type to the
|
|
specified process.
|
|
|
|
Arguments:
|
|
|
|
QuotaBlock - Supplies the quota block to return quota to.
|
|
|
|
PagedAmount - Supplies the amount of paged pool quota to return.
|
|
|
|
PagedAmount - Supplies the amount of non paged pool quota to return.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KIRQL OldIrql;
|
|
|
|
|
|
// if we bypassed the quota charge, don't do anything here either
|
|
|
|
|
|
if ( QuotaBlock == (PEPROCESS_QUOTA_BLOCK)1 ) {
|
|
return;
|
|
}
|
|
|
|
ExAcquireSpinLock(&QuotaBlock->QuotaLock,&OldIrql);
|
|
|
|
if ( PagedAmount ) {
|
|
|
|
if ( PagedAmount <= QuotaBlock->QuotaPoolUsage[PagedPool] ) {
|
|
QuotaBlock->QuotaPoolUsage[PagedPool] -= PagedAmount;
|
|
}
|
|
else {
|
|
ASSERT(FALSE);
|
|
QuotaBlock->QuotaPoolUsage[PagedPool] = 0;
|
|
}
|
|
}
|
|
if ( NonPagedAmount ) {
|
|
|
|
if ( NonPagedAmount <= QuotaBlock->QuotaPoolUsage[NonPagedPool] ) {
|
|
QuotaBlock->QuotaPoolUsage[NonPagedPool] -= NonPagedAmount;
|
|
}
|
|
else {
|
|
ASSERT(FALSE);
|
|
QuotaBlock->QuotaPoolUsage[NonPagedPool] = 0;
|
|
}
|
|
}
|
|
QuotaBlock->ReferenceCount--;
|
|
if ( QuotaBlock->ReferenceCount == 0 ) {
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
ExFreePool(QuotaBlock);
|
|
}
|
|
else {
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
PsChargePoolQuota(
|
|
IN PEPROCESS Process,
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T Amount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function charges pool quota of the specified pool type to
|
|
the specified process. If the quota charge would exceed the limits
|
|
allowed to the process, then an exception is raised and quota is
|
|
not charged.
|
|
|
|
Arguments:
|
|
|
|
Process - Supplies the process to charge quota to.
|
|
|
|
PoolType - Supplies the type of pool quota to charge.
|
|
|
|
Amount - Supplies the amount of pool quota to charge.
|
|
|
|
Return Value:
|
|
|
|
Raises STATUS_QUOTA_EXCEEDED if the quota charge would exceed the
|
|
limits allowed to the process.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KIRQL OldIrql;
|
|
SIZE_T NewPoolUsage;
|
|
PEPROCESS_QUOTA_BLOCK QuotaBlock;
|
|
SIZE_T NewLimit;
|
|
SIZE_T HardLimit;
|
|
|
|
ASSERT((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
|
|
|
|
QuotaBlock = Process->QuotaBlock;
|
|
|
|
retry_charge:
|
|
if ( QuotaBlock != &PspDefaultQuotaBlock ) {
|
|
ExAcquireSpinLock(&QuotaBlock->QuotaLock,&OldIrql);
|
|
do_charge:
|
|
NewPoolUsage = QuotaBlock->QuotaPoolUsage[PoolType] + Amount;
|
|
|
|
|
|
// See if enough quota exists in the block to satisfy the
|
|
// request
|
|
|
|
|
|
if ( NewPoolUsage > QuotaBlock->QuotaPoolLimit[PoolType] ) {
|
|
if ( PoolType == PagedPool ) {
|
|
HardLimit = PspDefaultPagedLimit;
|
|
}
|
|
else {
|
|
HardLimit = PspDefaultNonPagedLimit;
|
|
}
|
|
|
|
while ( (HardLimit == 0) && MmRaisePoolQuota(PoolType,QuotaBlock->QuotaPoolLimit[PoolType],&NewLimit) ) {
|
|
QuotaBlock->QuotaPoolLimit[PoolType] = NewLimit;
|
|
if ( NewPoolUsage <= NewLimit ) {
|
|
goto LimitRaised2;
|
|
}
|
|
}
|
|
|
|
//DbgPrint("PS: ChargePool Failed P %8x QB %8x PT %8x Amount %8x\n",Process,QuotaBlock,PoolType,Amount);DbgBreakPoint();
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
ExRaiseStatus(STATUS_QUOTA_EXCEEDED);
|
|
}
|
|
LimitRaised2:
|
|
if ( NewPoolUsage < QuotaBlock->QuotaPoolUsage[PoolType] ||
|
|
NewPoolUsage < Amount ) {
|
|
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
ExRaiseStatus(STATUS_QUOTA_EXCEEDED);
|
|
}
|
|
|
|
QuotaBlock->QuotaPoolUsage[PoolType] = NewPoolUsage;
|
|
if ( NewPoolUsage > QuotaBlock->QuotaPeakPoolUsage[PoolType] ) {
|
|
QuotaBlock->QuotaPeakPoolUsage[PoolType] = NewPoolUsage;
|
|
}
|
|
|
|
NewPoolUsage = Process->QuotaPoolUsage[PoolType] + Amount;
|
|
Process->QuotaPoolUsage[PoolType] = NewPoolUsage;
|
|
if ( NewPoolUsage > Process->QuotaPeakPoolUsage[PoolType] ) {
|
|
Process->QuotaPeakPoolUsage[PoolType] = NewPoolUsage;
|
|
}
|
|
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
}
|
|
else {
|
|
if ( Process == PsInitialSystemProcess ) {
|
|
return;
|
|
}
|
|
|
|
ExAcquireSpinLock(&PspDefaultQuotaBlock.QuotaLock,&OldIrql);
|
|
if ( (QuotaBlock = Process->QuotaBlock) != &PspDefaultQuotaBlock ) {
|
|
ExReleaseSpinLock(&PspDefaultQuotaBlock.QuotaLock,OldIrql);
|
|
goto retry_charge;
|
|
}
|
|
goto do_charge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VOID
|
|
PsReturnPoolQuota(
|
|
IN PEPROCESS Process,
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T Amount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns pool quota of the specified pool type to the
|
|
specified process.
|
|
|
|
Arguments:
|
|
|
|
Process - Supplies the process to return quota to.
|
|
|
|
PoolType - Supplies the type of pool quota to return.
|
|
|
|
Amount - Supplies the amount of pool quota to return
|
|
|
|
Return Value:
|
|
|
|
Raises STATUS_QUOTA_EXCEEDED if the quota charge would exceed the
|
|
limits allowed to the process.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
KIRQL OldIrql;
|
|
PEPROCESS_QUOTA_BLOCK QuotaBlock;
|
|
SIZE_T GiveBackLimit;
|
|
SIZE_T RoundedUsage;
|
|
|
|
ASSERT((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
|
|
|
|
QuotaBlock = Process->QuotaBlock;
|
|
retry_return:
|
|
if ( QuotaBlock != &PspDefaultQuotaBlock) {
|
|
ExAcquireSpinLock(&QuotaBlock->QuotaLock,&OldIrql);
|
|
|
|
if ( PspDoingGiveBacks ) {
|
|
if ( PoolType == PagedPool ) {
|
|
GiveBackLimit = MMPAGED_QUOTA_INCREASE;
|
|
}
|
|
else {
|
|
GiveBackLimit = MMNONPAGED_QUOTA_INCREASE;
|
|
}
|
|
}
|
|
else {
|
|
GiveBackLimit = 0;
|
|
}
|
|
do_return:
|
|
if ( Amount <= Process->QuotaPoolUsage[PoolType] ) {
|
|
Process->QuotaPoolUsage[PoolType] -= Amount;
|
|
}
|
|
else {
|
|
ASSERT(FALSE);
|
|
GiveBackLimit = 0;
|
|
Process->QuotaPoolUsage[PoolType] = 0;
|
|
}
|
|
|
|
if ( Amount <= QuotaBlock->QuotaPoolUsage[PoolType] ) {
|
|
QuotaBlock->QuotaPoolUsage[PoolType] -= Amount;
|
|
}
|
|
else {
|
|
ASSERT(FALSE);
|
|
GiveBackLimit = 0;
|
|
QuotaBlock->QuotaPoolUsage[PoolType] = 0;
|
|
}
|
|
|
|
if ( GiveBackLimit ) {
|
|
if (QuotaBlock->QuotaPoolLimit[PoolType] - QuotaBlock->QuotaPoolUsage[PoolType] > GiveBackLimit ) {
|
|
|
|
|
|
// round up the current usage to a page multiple
|
|
|
|
|
|
RoundedUsage = ROUND_TO_PAGES(QuotaBlock->QuotaPoolUsage[PoolType]);
|
|
|
|
|
|
// Give back the limit minus the rounded usage
|
|
|
|
|
|
GiveBackLimit = QuotaBlock->QuotaPoolLimit[PoolType] - RoundedUsage;
|
|
QuotaBlock->QuotaPoolLimit[PoolType] -= GiveBackLimit;
|
|
MmReturnPoolQuota(PoolType,GiveBackLimit);
|
|
|
|
}
|
|
}
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
}
|
|
else {
|
|
|
|
if ( Process == PsInitialSystemProcess ) {
|
|
return;
|
|
}
|
|
|
|
ExAcquireSpinLock(&PspDefaultQuotaBlock.QuotaLock,&OldIrql);
|
|
if ( (QuotaBlock = Process->QuotaBlock) != &PspDefaultQuotaBlock) {
|
|
ExReleaseSpinLock(&PspDefaultQuotaBlock.QuotaLock,OldIrql);
|
|
goto retry_return;
|
|
}
|
|
GiveBackLimit = 0;
|
|
goto do_return;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
PspInheritQuota(
|
|
IN PEPROCESS NewProcess,
|
|
IN PEPROCESS ParentProcess
|
|
)
|
|
{
|
|
PEPROCESS_QUOTA_BLOCK QuotaBlock;
|
|
KIRQL OldIrql;
|
|
|
|
if ( ParentProcess ) {
|
|
QuotaBlock = ParentProcess->QuotaBlock;
|
|
}
|
|
else {
|
|
QuotaBlock = &PspDefaultQuotaBlock;
|
|
}
|
|
|
|
ExAcquireSpinLock(&QuotaBlock->QuotaLock,&OldIrql);
|
|
QuotaBlock->ReferenceCount++;
|
|
NewProcess->QuotaBlock = QuotaBlock;
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
}
|
|
|
|
VOID
|
|
MiReturnPageFileQuota (
|
|
IN SIZE_T QuotaCharge,
|
|
IN PEPROCESS CurrentProcess
|
|
);
|
|
|
|
VOID
|
|
PspDereferenceQuota(
|
|
IN PEPROCESS Process
|
|
)
|
|
{
|
|
PEPROCESS_QUOTA_BLOCK QuotaBlock;
|
|
KIRQL OldIrql;
|
|
|
|
QuotaBlock = Process->QuotaBlock;
|
|
|
|
PsReturnPoolQuota(Process,NonPagedPool,Process->QuotaPoolUsage[NonPagedPool]);
|
|
PsReturnPoolQuota(Process,PagedPool,Process->QuotaPoolUsage[PagedPool]);
|
|
MiReturnPageFileQuota(Process->PagefileUsage,Process);
|
|
|
|
ExAcquireSpinLock(&QuotaBlock->QuotaLock,&OldIrql);
|
|
|
|
QuotaBlock->ReferenceCount--;
|
|
if ( QuotaBlock->ReferenceCount == 0 ) {
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
ExFreePool(QuotaBlock);
|
|
}
|
|
else {
|
|
ExReleaseSpinLock(&QuotaBlock->QuotaLock,OldIrql);
|
|
}
|
|
}
|