Windows2000/private/ntos/ps/psquota.c
2020-09-30 17:12:32 +02:00

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);
}
}