1292 lines
30 KiB
C
1292 lines
30 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:
|
||
|
|
||
|
Neill Clift (NeillC) 4-Nov-2000
|
||
|
|
||
|
Changed to be mostly lock free. Preserved the basic design in terms of how quota is managed.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "psp.h"
|
||
|
|
||
|
LIST_ENTRY PspQuotaBlockList; // List of all quota blocks except the default
|
||
|
|
||
|
#ifdef ALLOC_PRAGMA
|
||
|
#pragma alloc_text (INIT, PsInitializeQuotaSystem)
|
||
|
#pragma alloc_text (PAGE, PspInheritQuota)
|
||
|
#pragma alloc_text (PAGE, PspDereferenceQuota)
|
||
|
#pragma alloc_text (PAGE, PsChargeSharedPoolQuota)
|
||
|
#pragma alloc_text (PAGE, PsReturnSharedPoolQuota)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
VOID
|
||
|
PsInitializeQuotaSystem (
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function initializes the quota system.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KeInitializeSpinLock(&PspQuotaLock);
|
||
|
|
||
|
PspDefaultQuotaBlock.ReferenceCount = 1;
|
||
|
PspDefaultQuotaBlock.ProcessCount = 1;
|
||
|
PspDefaultQuotaBlock.QuotaEntry[PsPagedPool].Limit = (SIZE_T)-1;
|
||
|
PspDefaultQuotaBlock.QuotaEntry[PsNonPagedPool].Limit = (SIZE_T)-1;
|
||
|
PspDefaultQuotaBlock.QuotaEntry[PsPageFile].Limit = (SIZE_T)-1;
|
||
|
|
||
|
PsGetCurrentProcess()->QuotaBlock = &PspDefaultQuotaBlock;
|
||
|
|
||
|
InitializeListHead (&PspQuotaBlockList);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PspInsertQuotaBlock (
|
||
|
IN PEPROCESS_QUOTA_BLOCK QuotaBlock
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routines as a new quota block to the global list of system quota blocks.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
QuotaBlock - Quota block to be inserted into the list.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
ExAcquireSpinLock (&PspQuotaLock, &OldIrql);
|
||
|
InsertTailList (&PspQuotaBlockList, &QuotaBlock->QuotaList);
|
||
|
ExReleaseSpinLock (&PspQuotaLock, OldIrql);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PspDereferenceQuotaBlock (
|
||
|
IN PEPROCESS_QUOTA_BLOCK QuotaBlock
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This removes a single reference from a quota block and deletes the block if it was the last.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
QuotaBlock - Quota block to dereference
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
KIRQL OldIrql;
|
||
|
SIZE_T ReturnQuota;
|
||
|
PS_QUOTA_TYPE QuotaType;
|
||
|
|
||
|
if (InterlockedDecrement ((PLONG) &QuotaBlock->ReferenceCount) == 0) {
|
||
|
|
||
|
ExAcquireSpinLock (&PspQuotaLock, &OldIrql);
|
||
|
|
||
|
RemoveEntryList (&QuotaBlock->QuotaList);
|
||
|
|
||
|
//
|
||
|
// Free any unreturned quota;
|
||
|
//
|
||
|
for (QuotaType = PsNonPagedPool;
|
||
|
QuotaType <= PsPagedPool;
|
||
|
QuotaType++) {
|
||
|
ReturnQuota = QuotaBlock->QuotaEntry[QuotaType].Return + QuotaBlock->QuotaEntry[QuotaType].Limit;
|
||
|
if (ReturnQuota > 0) {
|
||
|
MmReturnPoolQuota (QuotaType, ReturnQuota);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExReleaseSpinLock (&PspQuotaLock, OldIrql);
|
||
|
|
||
|
ExFreePool (QuotaBlock);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SIZE_T
|
||
|
FORCEINLINE
|
||
|
PspInterlockedExchangeQuota (
|
||
|
IN PSIZE_T pQuota,
|
||
|
IN SIZE_T NewQuota)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function does an interlocked exchange on a quota variable.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pQuota - Pointer to a quota entry to exchange into
|
||
|
|
||
|
NewQuota - The new value to exchange into the quota location.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
SIZE_T - Old value that was contained in the quota variable
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
#if !defined(_WIN64)
|
||
|
return InterlockedExchange ((PLONG) pQuota, NewQuota);
|
||
|
#else
|
||
|
return InterlockedExchange64 ((PLONGLONG) pQuota, NewQuota);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
SIZE_T
|
||
|
FORCEINLINE
|
||
|
PspInterlockedCompareExchangeQuota (
|
||
|
IN PSIZE_T pQuota,
|
||
|
IN SIZE_T NewQuota,
|
||
|
IN SIZE_T OldQuota
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function performs a compare exchange operation on a quota variable
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pQuota - Pointer to the quota variable being changed
|
||
|
NewQuota - New value to place in the quota variable
|
||
|
OldQuota - The current contents of the quota variable
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
SIZE_T - The old contents of the variable
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
#if !defined(_WIN64)
|
||
|
return InterlockedCompareExchange ((PLONG) pQuota, NewQuota, OldQuota);
|
||
|
#else
|
||
|
return InterlockedCompareExchange64 ((PLONGLONG)pQuota, NewQuota, OldQuota);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
SIZE_T
|
||
|
PspReleaseReturnedQuota (
|
||
|
IN PS_QUOTA_TYPE QuotaType
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function walks the list of system quota blocks and returns any non-returned quota.
|
||
|
This function is called when we are about to fail a quota charge and we want to try and
|
||
|
free some resources up.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
QuotaType - Type of quota to scan for.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
SIZE_T - Amount of that quota returned to the system.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
SIZE_T ReturnQuota, Usage, Limit;
|
||
|
PLIST_ENTRY ListEntry;
|
||
|
PEPROCESS_QUOTA_BLOCK QuotaBlock;
|
||
|
|
||
|
ReturnQuota = 0;
|
||
|
ListEntry = PspQuotaBlockList.Flink;
|
||
|
while (1) {
|
||
|
if (ListEntry == &PspQuotaBlockList) {
|
||
|
break;
|
||
|
}
|
||
|
QuotaBlock = CONTAINING_RECORD (ListEntry, EPROCESS_QUOTA_BLOCK, QuotaList);
|
||
|
//
|
||
|
// Gather up any unreturned quota;
|
||
|
//
|
||
|
ReturnQuota += PspInterlockedExchangeQuota (&QuotaBlock->QuotaEntry[QuotaType].Return, 0);
|
||
|
//
|
||
|
// If no more processes are assocociated with this block then trim its limit back. This
|
||
|
// block can only have quota returned at this point.
|
||
|
//
|
||
|
if (QuotaBlock->ProcessCount == 0) {
|
||
|
Usage = QuotaBlock->QuotaEntry[QuotaType].Usage;
|
||
|
Limit = QuotaBlock->QuotaEntry[QuotaType].Limit;
|
||
|
if (Limit > Usage) {
|
||
|
if (PspInterlockedCompareExchangeQuota (&QuotaBlock->QuotaEntry[QuotaType].Limit,
|
||
|
Usage,
|
||
|
Limit) == Limit) {
|
||
|
ReturnQuota += Limit - Usage;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ListEntry = ListEntry->Flink;
|
||
|
|
||
|
}
|
||
|
if (ReturnQuota > 0) {
|
||
|
MmReturnPoolQuota (QuotaType, ReturnQuota);
|
||
|
}
|
||
|
|
||
|
return ReturnQuota;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Interfaces return different status values for differen quotas. These are the values.
|
||
|
//
|
||
|
const static NTSTATUS PspQuotaStatus[PsQuotaTypes] = {STATUS_QUOTA_EXCEEDED,
|
||
|
STATUS_QUOTA_EXCEEDED,
|
||
|
STATUS_PAGEFILE_QUOTA_EXCEEDED};
|
||
|
|
||
|
VOID
|
||
|
FORCEINLINE
|
||
|
PspInterlockedMaxQuota (
|
||
|
IN PSIZE_T pQuota,
|
||
|
IN SIZE_T NewQuota
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function makes sure that the target contains a value that >= to the new quota value.
|
||
|
This is used to maintain peak values.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pQuota - Pointer to a quota variable
|
||
|
NewQuota - New value to be used in the maximum comparison.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
SIZE_T Quota;
|
||
|
|
||
|
Quota = *pQuota;
|
||
|
while (1) {
|
||
|
if (NewQuota <= Quota) {
|
||
|
break;
|
||
|
}
|
||
|
//
|
||
|
// This looks strange because we don't care if the exchanged suceeded. We only
|
||
|
// care that the quota is greater than our new quota.
|
||
|
//
|
||
|
Quota = PspInterlockedCompareExchangeQuota (pQuota,
|
||
|
NewQuota,
|
||
|
Quota);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SIZE_T
|
||
|
FORCEINLINE
|
||
|
PspInterlockedAddQuota (
|
||
|
IN PSIZE_T pQuota,
|
||
|
IN SIZE_T Amount
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function adds the specified amount on to the target quota
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pQuota - Pointer to a quota variable to be modified
|
||
|
Amount - Amount to be added to the quota
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
SIZE_T - New value of quota variable after the addition was performed
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
#if !defined(_WIN64)
|
||
|
return InterlockedExchangeAdd ((PLONG) pQuota, Amount) + Amount;
|
||
|
#else
|
||
|
SIZE_T Quota, NewQuota, tQuota;
|
||
|
|
||
|
Quota = *pQuota;
|
||
|
while (1) {
|
||
|
NewQuota = Quota + Amount;
|
||
|
tQuota = InterlockedCompareExchange64 ((PLONGLONG) pQuota,
|
||
|
NewQuota,
|
||
|
Quota);
|
||
|
if (tQuota == Quota) {
|
||
|
return NewQuota;
|
||
|
}
|
||
|
Quota = tQuota;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
SIZE_T
|
||
|
FORCEINLINE
|
||
|
PspInterlockedSubtractQuota (
|
||
|
IN PSIZE_T pUsage,
|
||
|
IN SIZE_T Amount
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function subtracts the specified amount on to the target quota
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pQuota - Pointer to a quota variable to be modified
|
||
|
Amount - Amount to be subtracted from the quota
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
SIZE_T - New value of quota variable after the subtraction was performed
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
#if !defined(_WIN64)
|
||
|
return InterlockedExchangeAdd ((PLONG) pUsage, -(LONG)Amount) - Amount;
|
||
|
#else
|
||
|
SIZE_T Usage, NewUsage, tUsage;
|
||
|
|
||
|
Usage = *pUsage;
|
||
|
while (1) {
|
||
|
NewUsage = Usage - Amount;
|
||
|
tUsage = InterlockedCompareExchange64 ((PLONGLONG) pUsage,
|
||
|
NewUsage,
|
||
|
Usage);
|
||
|
if (tUsage == Usage) {
|
||
|
return NewUsage;
|
||
|
}
|
||
|
Usage = tUsage;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOLEAN
|
||
|
PspExpandQuota (
|
||
|
IN PS_QUOTA_TYPE QuotaType,
|
||
|
IN PEPROCESS_QUOTA_ENTRY QE,
|
||
|
IN SIZE_T Usage,
|
||
|
IN SIZE_T Amount,
|
||
|
OUT SIZE_T *pLimit
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function charges the specified quota to a process quota block
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
QuotaType - The quota being charged. One of PsNonPagedPool, PsPagedPool or PsPageFile.
|
||
|
QE - Quota entry being modified
|
||
|
Usage - The current quota usage
|
||
|
Amount - The amount of quota being charged.
|
||
|
pLimit - The new limit
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
BOOLEAN - TRUE if quota expansion suceeded.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
SIZE_T Limit, NewLimit;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
//
|
||
|
// We need to attempt quota expansion for this request.
|
||
|
// Acquire the global lock and see if somebody else changed the limit.
|
||
|
// We don't want to do too many expansions. If somebody else did it
|
||
|
// then we want to use theirs if possible.
|
||
|
//
|
||
|
ExAcquireSpinLock (&PspQuotaLock, &OldIrql);
|
||
|
|
||
|
//
|
||
|
// Refetch limit information. Another thread may have done limit expansion/contraction.
|
||
|
// By refetching limit we preserve the order we established above.
|
||
|
//
|
||
|
Limit = QE->Limit;
|
||
|
|
||
|
//
|
||
|
// If the request could be satisfied now then repeat.
|
||
|
//
|
||
|
if (Usage + Amount <= Limit) {
|
||
|
ExReleaseSpinLock (&PspQuotaLock, OldIrql);
|
||
|
*pLimit = Limit;
|
||
|
return TRUE;
|
||
|
}
|
||
|
//
|
||
|
// If expansion is currently enabled then attempt it.
|
||
|
// If this fails then scavenge any returns from all the
|
||
|
// quota blocks in the system and try again.
|
||
|
//
|
||
|
if (((QuotaType == PsNonPagedPool)?PspDefaultNonPagedLimit:PspDefaultPagedLimit) == 0) {
|
||
|
if (MmRaisePoolQuota (QuotaType, Limit, &NewLimit) ||
|
||
|
(PspReleaseReturnedQuota (QuotaType) > 0 &&
|
||
|
MmRaisePoolQuota (QuotaType, Limit, &NewLimit))) {
|
||
|
//
|
||
|
// We refetch limit here but that doesn't violate the ordering
|
||
|
//
|
||
|
Limit = PspInterlockedAddQuota (&QE->Limit, NewLimit - Limit);
|
||
|
ExReleaseSpinLock (&PspQuotaLock, OldIrql);
|
||
|
*pLimit = Limit;
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExReleaseSpinLock (&PspQuotaLock, OldIrql);
|
||
|
|
||
|
*pLimit = Limit;
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
FORCEINLINE
|
||
|
PspChargeQuota (
|
||
|
IN PEPROCESS_QUOTA_BLOCK QuotaBlock,
|
||
|
IN PEPROCESS Process,
|
||
|
IN PS_QUOTA_TYPE QuotaType,
|
||
|
IN SIZE_T Amount)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function charges the specified quota to a process quota block
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
QuotaBlock - Quota block to make charges to.
|
||
|
Process - Process that is being charged.
|
||
|
QuotaType - The quota being charged. One of PsNonPagedPool, PsPagedPool or PsPageFile.
|
||
|
Amount - The amount of quota being charged.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - Status of the operation
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PEPROCESS_QUOTA_ENTRY QE;
|
||
|
SIZE_T Usage, Limit, NewUsage, tUsage, Extra;
|
||
|
|
||
|
QE = &QuotaBlock->QuotaEntry[QuotaType];
|
||
|
|
||
|
//
|
||
|
// This memory barrier is important. In order not to have to recheck the limit after
|
||
|
// we charge the quota we only ever reduce the limit by the same amount we are about
|
||
|
// reduce the usage by. Using an out of data limit will only allow us to over charge
|
||
|
// by an amount another thread is just about to release.
|
||
|
//
|
||
|
Usage = *(volatile SIZE_T *)&QE->Usage;
|
||
|
|
||
|
KeMemoryBarrier ();
|
||
|
|
||
|
Limit = *(volatile SIZE_T *)&QE->Limit;
|
||
|
while (1) {
|
||
|
NewUsage = Usage + Amount;
|
||
|
//
|
||
|
// Wrapping cases are always rejected
|
||
|
//
|
||
|
if (NewUsage < Usage) {
|
||
|
return PspQuotaStatus [QuotaType];
|
||
|
}
|
||
|
//
|
||
|
// If its within the limits then try and grab the quota
|
||
|
//
|
||
|
if (NewUsage <= Limit) {
|
||
|
tUsage = PspInterlockedCompareExchangeQuota (&QE->Usage,
|
||
|
NewUsage,
|
||
|
Usage);
|
||
|
if (tUsage == Usage) {
|
||
|
//
|
||
|
// Update the Peak value
|
||
|
//
|
||
|
PspInterlockedMaxQuota (&QE->Peak, NewUsage);
|
||
|
//
|
||
|
// Update the process counts if needed
|
||
|
//
|
||
|
if (Process != NULL) {
|
||
|
NewUsage = PspInterlockedAddQuota (&Process->QuotaUsage[QuotaType], Amount);
|
||
|
//
|
||
|
// Update the peak value
|
||
|
//
|
||
|
PspInterlockedMaxQuota (&Process->QuotaPeak[QuotaType], NewUsage);
|
||
|
}
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
//
|
||
|
// The usage has changed under us. We have a new usage from the exchange
|
||
|
// but must refetch the limit to preserve the ordering we established
|
||
|
// above this loop. We don't need a memory barrier as we obtained
|
||
|
// the new value via an interlocked operation and they contain barriers.
|
||
|
//
|
||
|
Usage = tUsage;
|
||
|
Limit = *(volatile SIZE_T *) &QE->Limit;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Page file quota is not increased
|
||
|
//
|
||
|
if (QuotaType == PsPageFile) {
|
||
|
return PspQuotaStatus [QuotaType];
|
||
|
} else {
|
||
|
//
|
||
|
// First try and grab any returns that this process has made.
|
||
|
//
|
||
|
Extra = PspInterlockedExchangeQuota (&QE->Return, 0);
|
||
|
if (Extra > 0) {
|
||
|
//
|
||
|
// We had some returns so add this to the limit. We can retry the
|
||
|
// acquire with the new limit. We refetch the limit here but that
|
||
|
// doesn't violate the state we set up at the top of the loop.
|
||
|
// The state is that we read the Usage before we read the limit.
|
||
|
//
|
||
|
Limit = PspInterlockedAddQuota (&QE->Limit, Extra);
|
||
|
continue;
|
||
|
}
|
||
|
//
|
||
|
// Try to expand quota if we can
|
||
|
//
|
||
|
if (PspExpandQuota (QuotaType, QE, Usage, Amount, &Limit)) {
|
||
|
//
|
||
|
// We refetched limit here but that doesn't violate the ordering
|
||
|
//
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
return PspQuotaStatus [QuotaType];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PspGivebackQuota (
|
||
|
IN PS_QUOTA_TYPE QuotaType,
|
||
|
IN PEPROCESS_QUOTA_ENTRY QE
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function returns excess freed quota to MM
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
QuotaType - The quota being returned. One of PsNonPagedPool, PsPagedPool or PsPageFile.
|
||
|
QE - Quote entry to return to
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
SIZE_T GiveBack;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
//
|
||
|
// Acquire a global spinlock so we only have one thread giving back to the system
|
||
|
//
|
||
|
ExAcquireSpinLock (&PspQuotaLock, &OldIrql);
|
||
|
GiveBack = PspInterlockedExchangeQuota (&QE->Return, 0);
|
||
|
if (GiveBack > 0) {
|
||
|
MmReturnPoolQuota (QuotaType, GiveBack);
|
||
|
}
|
||
|
ExReleaseSpinLock (&PspQuotaLock, OldIrql);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
FORCEINLINE
|
||
|
PspReturnQuota (
|
||
|
IN PEPROCESS_QUOTA_BLOCK QuotaBlock,
|
||
|
IN PEPROCESS Process,
|
||
|
IN PS_QUOTA_TYPE QuotaType,
|
||
|
IN SIZE_T Amount)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function returns previously charged quota to the quota block
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
QuotaBlock - Quota block to return charges to.
|
||
|
Process - Process that was originaly charged.
|
||
|
QuotaType - The quota being returned. One of PsNonPagedPool, PsPagedPool or PsPageFile.
|
||
|
Amount - The amount of quota being returned.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PEPROCESS_QUOTA_ENTRY QE;
|
||
|
SIZE_T Usage, NewUsage, tUsage, tAmount, rAmount, Limit, NewLimit, tLimit;
|
||
|
SIZE_T GiveBackLimit, GiveBack;
|
||
|
|
||
|
QE = &QuotaBlock->QuotaEntry[QuotaType];
|
||
|
|
||
|
Usage = QE->Usage;
|
||
|
Limit = QE->Limit;
|
||
|
//
|
||
|
// We need to give back quota here if we have lots to return.
|
||
|
//
|
||
|
#define PSMINGIVEBACK ((MMPAGED_QUOTA_INCREASE > MMNONPAGED_QUOTA_INCREASE)?MMNONPAGED_QUOTA_INCREASE:MMPAGED_QUOTA_INCREASE)
|
||
|
if (Limit - Usage > PSMINGIVEBACK && Limit > Usage) {
|
||
|
if (QuotaType != PsPageFile && QuotaBlock != &PspDefaultQuotaBlock && PspDoingGiveBacks) {
|
||
|
if (QuotaType == PsPagedPool) {
|
||
|
GiveBackLimit = MMPAGED_QUOTA_INCREASE;
|
||
|
} else {
|
||
|
GiveBackLimit = MMNONPAGED_QUOTA_INCREASE;
|
||
|
}
|
||
|
if (GiveBackLimit > Amount) {
|
||
|
GiveBack = Amount;
|
||
|
} else {
|
||
|
GiveBack = GiveBackLimit;
|
||
|
}
|
||
|
NewLimit = Limit - GiveBack;
|
||
|
tLimit = PspInterlockedCompareExchangeQuota (&QE->Limit,
|
||
|
NewLimit,
|
||
|
Limit);
|
||
|
|
||
|
if (tLimit == Limit) {
|
||
|
//
|
||
|
// We suceeded in shrinking the limit. Add this reduction to the return field.
|
||
|
// If returns exceed a threshhold then give the lot bacxk to MM.
|
||
|
//
|
||
|
GiveBack = PspInterlockedAddQuota (&QE->Return, GiveBack);
|
||
|
if (GiveBack > GiveBackLimit) {
|
||
|
PspGivebackQuota (QuotaType, QE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now return the quota to the usage field.
|
||
|
// The charge might have been split across the default quota block and
|
||
|
// a new quota block. We have to handle this case here by first returning
|
||
|
// quota to the specified quota block then skipping to the default.
|
||
|
//
|
||
|
rAmount = Amount;
|
||
|
while (1) {
|
||
|
if (rAmount > Usage) {
|
||
|
tAmount = Usage;
|
||
|
NewUsage = 0;
|
||
|
} else {
|
||
|
tAmount = rAmount;
|
||
|
NewUsage = Usage - rAmount;
|
||
|
}
|
||
|
|
||
|
tUsage = PspInterlockedCompareExchangeQuota (&QE->Usage,
|
||
|
NewUsage,
|
||
|
Usage);
|
||
|
if (tUsage == Usage) {
|
||
|
//
|
||
|
// Update the process counts if needed
|
||
|
//
|
||
|
if (Process != NULL) {
|
||
|
ASSERT (tAmount <= Process->QuotaUsage[QuotaType]);
|
||
|
NewUsage = PspInterlockedSubtractQuota (&Process->QuotaUsage[QuotaType], tAmount);
|
||
|
}
|
||
|
rAmount = rAmount - tAmount;
|
||
|
if (rAmount == 0) {
|
||
|
return;
|
||
|
}
|
||
|
ASSERT (QuotaBlock != &PspDefaultQuotaBlock);
|
||
|
if (QuotaBlock == &PspDefaultQuotaBlock) {
|
||
|
return;
|
||
|
}
|
||
|
QuotaBlock = &PspDefaultQuotaBlock;
|
||
|
QE = &QuotaBlock->QuotaEntry[QuotaType];
|
||
|
Usage = QE->Usage;
|
||
|
} else {
|
||
|
Usage = tUsage;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PEPROCESS_QUOTA_BLOCK QuotaBlock;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
ASSERT((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
|
||
|
|
||
|
if (Process == PsInitialSystemProcess) {
|
||
|
return (PEPROCESS_QUOTA_BLOCK) 1;
|
||
|
}
|
||
|
|
||
|
QuotaBlock = Process->QuotaBlock;
|
||
|
|
||
|
if (PagedAmount > 0) {
|
||
|
Status = PspChargeQuota (QuotaBlock, NULL, PsPagedPool, PagedAmount);
|
||
|
if (!NT_SUCCESS (Status)) {
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
if (NonPagedAmount > 0) {
|
||
|
Status = PspChargeQuota (QuotaBlock, NULL, PsNonPagedPool, NonPagedAmount);
|
||
|
if (!NT_SUCCESS (Status)) {
|
||
|
if (PagedAmount > 0) {
|
||
|
PspReturnQuota (QuotaBlock, NULL, PsPagedPool, PagedAmount);
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
InterlockedIncrement ((PLONG) &QuotaBlock->ReferenceCount);
|
||
|
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.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
//
|
||
|
// if we bypassed the quota charge, don't do anything here either
|
||
|
//
|
||
|
|
||
|
if (QuotaBlock == (PEPROCESS_QUOTA_BLOCK) 1) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (PagedAmount > 0) {
|
||
|
PspReturnQuota (QuotaBlock, NULL, PsPagedPool, PagedAmount);
|
||
|
}
|
||
|
|
||
|
if (NonPagedAmount > 0) {
|
||
|
PspReturnQuota (QuotaBlock, NULL, PsNonPagedPool, NonPagedAmount);
|
||
|
}
|
||
|
|
||
|
PspDereferenceQuotaBlock (QuotaBlock);
|
||
|
}
|
||
|
|
||
|
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.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
Status = PsChargeProcessPoolQuota (Process,
|
||
|
PoolType,
|
||
|
Amount);
|
||
|
if (!NT_SUCCESS (Status)) {
|
||
|
ExRaiseStatus (Status);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
PsChargeProcessPoolQuota(
|
||
|
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:
|
||
|
|
||
|
NTSTATUS - Status of operation
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
ASSERT ((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
|
||
|
|
||
|
ASSERT (PoolType == PagedPool || PoolType == NonPagedPool);
|
||
|
|
||
|
__assume (PoolType == PagedPool || PoolType == NonPagedPool);
|
||
|
|
||
|
|
||
|
if (Process == PsInitialSystemProcess) {
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
return PspChargeQuota (Process->QuotaBlock, Process, PoolType, Amount);
|
||
|
}
|
||
|
|
||
|
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.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
ASSERT((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
|
||
|
|
||
|
ASSERT (PoolType == PagedPool || PoolType == NonPagedPool);
|
||
|
|
||
|
__assume (PoolType == PagedPool || PoolType == NonPagedPool);
|
||
|
|
||
|
if (Process == PsInitialSystemProcess) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
PspReturnQuota (Process->QuotaBlock, Process, PoolType, Amount);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PspInheritQuota(
|
||
|
IN PEPROCESS NewProcess,
|
||
|
IN PEPROCESS ParentProcess
|
||
|
)
|
||
|
{
|
||
|
PEPROCESS_QUOTA_BLOCK QuotaBlock;
|
||
|
|
||
|
if (ParentProcess) {
|
||
|
QuotaBlock = ParentProcess->QuotaBlock;
|
||
|
} else {
|
||
|
QuotaBlock = &PspDefaultQuotaBlock;
|
||
|
}
|
||
|
|
||
|
InterlockedIncrement ((PLONG) &QuotaBlock->ReferenceCount);
|
||
|
InterlockedIncrement ((PLONG) &QuotaBlock->ProcessCount);
|
||
|
NewProcess->QuotaBlock = QuotaBlock;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PspDereferenceQuota (
|
||
|
IN PEPROCESS Process
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is called at process object deletion to remove the quota block.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Process - Supplies the process to return quota to.
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PEPROCESS_QUOTA_BLOCK QuotaBlock;
|
||
|
|
||
|
ASSERT (Process->QuotaUsage[PsNonPagedPool] == 0);
|
||
|
|
||
|
ASSERT (Process->QuotaUsage[PsPagedPool] == 0);
|
||
|
|
||
|
ASSERT (Process->QuotaUsage[PsPageFile] == 0);
|
||
|
|
||
|
QuotaBlock = Process->QuotaBlock;
|
||
|
|
||
|
InterlockedDecrement ((PLONG) &QuotaBlock->ProcessCount);
|
||
|
PspDereferenceQuotaBlock (QuotaBlock);
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
PsChargeProcessQuota (
|
||
|
IN PEPROCESS Process,
|
||
|
IN PS_QUOTA_TYPE QuotaType,
|
||
|
IN SIZE_T Amount
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is called to charge against the specified quota.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Process - Supplies the process to charge against.
|
||
|
QuotaType - Type of quota being charged
|
||
|
Amount - Amount of quota being charged
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - Status of operation
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ASSERT ((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
|
||
|
|
||
|
if (Process == PsInitialSystemProcess) {
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
return PspChargeQuota (Process->QuotaBlock, Process, QuotaType, Amount);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PsReturnProcessQuota (
|
||
|
IN PEPROCESS Process,
|
||
|
IN PS_QUOTA_TYPE QuotaType,
|
||
|
IN SIZE_T Amount
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is called to return previously charged quota to the specified process
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Process - Supplies the process that was previously charged.
|
||
|
QuotaType - Type of quota being returned
|
||
|
Amount - Amount of quota being returned
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - Status of operation
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ASSERT ((Process->Pcb.Header.Type == ProcessObject) || (Process->Pcb.Header.Type == 0));
|
||
|
|
||
|
if (Process == PsInitialSystemProcess) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
PspReturnQuota (Process->QuotaBlock, Process, QuotaType, Amount);
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
PsChargeProcessNonPagedPoolQuota(
|
||
|
IN PEPROCESS Process,
|
||
|
IN SIZE_T Amount
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is called to charge non-paged pool quota against the specified process.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Process - Supplies the process to charge against.
|
||
|
Amount - Amount of quota being charged
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - Status of operation
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (Process == PsInitialSystemProcess) {
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
return PspChargeQuota (Process->QuotaBlock, Process, PsNonPagedPool, Amount);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PsReturnProcessNonPagedPoolQuota(
|
||
|
IN PEPROCESS Process,
|
||
|
IN SIZE_T Amount
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is called to return previously charged non-paged pool quota to the specified process
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Process - Supplies the process that was previously charged.
|
||
|
Amount - Amount of quota being returned
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - Status of operation
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (Process == PsInitialSystemProcess) {
|
||
|
return;
|
||
|
}
|
||
|
PspReturnQuota (Process->QuotaBlock, Process, PsNonPagedPool, Amount);
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
PsChargeProcessPagedPoolQuota(
|
||
|
IN PEPROCESS Process,
|
||
|
IN SIZE_T Amount
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is called to charge paged pool quota against the specified process.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Process - Supplies the process to charge against.
|
||
|
Amount - Amount of quota being charged
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - Status of operation
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (Process == PsInitialSystemProcess) {
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
return PspChargeQuota (Process->QuotaBlock, Process, PsPagedPool, Amount);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PsReturnProcessPagedPoolQuota(
|
||
|
IN PEPROCESS Process,
|
||
|
IN SIZE_T Amount
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is called to return previously charged paged pool quota to the specified process
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Process - Supplies the process that was previously charged.
|
||
|
Amount - Amount of quota being returned
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - Status of operation
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (Process == PsInitialSystemProcess) {
|
||
|
return;
|
||
|
}
|
||
|
PspReturnQuota (Process->QuotaBlock, Process, PsPagedPool, Amount);
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
PsChargeProcessPageFileQuota(
|
||
|
IN PEPROCESS Process,
|
||
|
IN SIZE_T Amount
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is called to charge page file quota against the specified process.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Process - Supplies the process to charge against.
|
||
|
Amount - Amount of quota being charged
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - Status of operation
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (Process == PsInitialSystemProcess) {
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
return PspChargeQuota (Process->QuotaBlock, Process, PsPageFile, Amount);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PsReturnProcessPageFileQuota(
|
||
|
IN PEPROCESS Process,
|
||
|
IN SIZE_T Amount
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is called to return previously charged page file quota to the specified process
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Process - Supplies the process that was previously charged.
|
||
|
Amount - Amount of quota being returned
|
||
|
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - Status of operation
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if (Process == PsInitialSystemProcess) {
|
||
|
return;
|
||
|
}
|
||
|
PspReturnQuota (Process->QuotaBlock, Process, PsPageFile, Amount);
|
||
|
}
|