[NTOS:PS] Implement PS_QUOTA_TYPE and let the quota code use it (#3389)

This will replace the PoolIndex variable and as such we'll only be using the PS_QUOTA_TYPE enumeration, as Windows does. Both QuotaEntry, QuotaUsage and QuotaPeak depend explicitly or implicitly on this enumeration. Further details about this enum can be found in the following articles.
https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ps/psquota/type.htm?tx=68,143
https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ps/psquota/block.htm?tx=68,142,143
https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/ps/eprocess/index.htm (see QuotaPeak and QuotaUsage)
This commit is contained in:
George Bișoc 2021-03-02 20:09:58 +01:00 committed by GitHub
parent 9536f44c81
commit 6170b574f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 43 deletions

View file

@ -1025,12 +1025,12 @@ QSI_DEF(SystemProcessInformation)
SpiCurrent->PageFaultCount = Process->Vm.PageFaultCount; SpiCurrent->PageFaultCount = Process->Vm.PageFaultCount;
SpiCurrent->PeakWorkingSetSize = Process->Vm.PeakWorkingSetSize; SpiCurrent->PeakWorkingSetSize = Process->Vm.PeakWorkingSetSize;
SpiCurrent->WorkingSetSize = Process->Vm.WorkingSetSize; SpiCurrent->WorkingSetSize = Process->Vm.WorkingSetSize;
SpiCurrent->QuotaPeakPagedPoolUsage = Process->QuotaPeak[0]; SpiCurrent->QuotaPeakPagedPoolUsage = Process->QuotaPeak[PsPagedPool];
SpiCurrent->QuotaPagedPoolUsage = Process->QuotaUsage[0]; SpiCurrent->QuotaPagedPoolUsage = Process->QuotaUsage[PsPagedPool];
SpiCurrent->QuotaPeakNonPagedPoolUsage = Process->QuotaPeak[1]; SpiCurrent->QuotaPeakNonPagedPoolUsage = Process->QuotaPeak[PsNonPagedPool];
SpiCurrent->QuotaNonPagedPoolUsage = Process->QuotaUsage[1]; SpiCurrent->QuotaNonPagedPoolUsage = Process->QuotaUsage[PsNonPagedPool];
SpiCurrent->PagefileUsage = Process->QuotaUsage[2]; SpiCurrent->PagefileUsage = Process->QuotaUsage[PsPageFile];
SpiCurrent->PeakPagefileUsage = Process->QuotaPeak[2]; SpiCurrent->PeakPagefileUsage = Process->QuotaPeak[PsPageFile];
SpiCurrent->PrivatePageCount = Process->CommitCharge; SpiCurrent->PrivatePageCount = Process->CommitCharge;
ThreadInfo = (PSYSTEM_THREAD_INFORMATION)(SpiCurrent + 1); ThreadInfo = (PSYSTEM_THREAD_INFORMATION)(SpiCurrent + 1);

View file

@ -460,12 +460,12 @@ NtQueryInformationProcess(IN HANDLE ProcessHandle,
VmCounters->PageFaultCount = Process->Vm.PageFaultCount; VmCounters->PageFaultCount = Process->Vm.PageFaultCount;
VmCounters->PeakWorkingSetSize = Process->Vm.PeakWorkingSetSize; VmCounters->PeakWorkingSetSize = Process->Vm.PeakWorkingSetSize;
VmCounters->WorkingSetSize = Process->Vm.WorkingSetSize; VmCounters->WorkingSetSize = Process->Vm.WorkingSetSize;
VmCounters->QuotaPeakPagedPoolUsage = Process->QuotaPeak[0]; VmCounters->QuotaPeakPagedPoolUsage = Process->QuotaPeak[PsPagedPool];
VmCounters->QuotaPagedPoolUsage = Process->QuotaUsage[0]; VmCounters->QuotaPagedPoolUsage = Process->QuotaUsage[PsPagedPool];
VmCounters->QuotaPeakNonPagedPoolUsage = Process->QuotaPeak[1]; VmCounters->QuotaPeakNonPagedPoolUsage = Process->QuotaPeak[PsNonPagedPool];
VmCounters->QuotaNonPagedPoolUsage = Process->QuotaUsage[1]; VmCounters->QuotaNonPagedPoolUsage = Process->QuotaUsage[PsNonPagedPool];
VmCounters->PagefileUsage = Process->QuotaUsage[2] << PAGE_SHIFT; VmCounters->PagefileUsage = Process->QuotaUsage[PsPageFile] << PAGE_SHIFT;
VmCounters->PeakPagefileUsage = Process->QuotaPeak[2] << PAGE_SHIFT; VmCounters->PeakPagefileUsage = Process->QuotaPeak[PsPageFile] << PAGE_SHIFT;
//VmCounters->PrivateUsage = Process->CommitCharge << PAGE_SHIFT; //VmCounters->PrivateUsage = Process->CommitCharge << PAGE_SHIFT;
// //

View file

@ -28,38 +28,36 @@ static KSPIN_LOCK PspQuotaLock;
/* /*
* Private helper to charge the specified process quota. * Private helper to charge the specified process quota.
* ReturnsSTATUS_QUOTA_EXCEEDED on quota limit check failure. * Returns STATUS_QUOTA_EXCEEDED on quota limit check failure.
* Updates QuotaPeak as needed for specified PoolIndex. * Updates QuotaPeak as needed for specified quota type in PS_QUOTA_TYPE enum.
* TODO: Research and possibly add (the undocumented) enum type PS_QUOTA_TYPE
* to replace UCHAR for 'PoolIndex'.
* Notes: Conceptually translation unit local/private. * Notes: Conceptually translation unit local/private.
*/ */
NTSTATUS NTSTATUS
NTAPI NTAPI
PspChargeProcessQuotaSpecifiedPool(IN PEPROCESS Process, PspChargeProcessQuotaSpecifiedPool(IN PEPROCESS Process,
IN UCHAR PoolIndex, IN PS_QUOTA_TYPE QuotaType,
IN SIZE_T Amount) IN SIZE_T Amount)
{ {
ASSERT(Process); ASSERT(Process);
ASSERT(Process != PsInitialSystemProcess); ASSERT(Process != PsInitialSystemProcess);
ASSERT(PoolIndex <= 2); ASSERT(QuotaType < PsQuotaTypes);
ASSERT(Process->QuotaBlock); ASSERT(Process->QuotaBlock);
/* Note: Race warning. TODO: Needs to add/use lock for this */ /* Note: Race warning. TODO: Needs to add/use lock for this */
if (Process->QuotaUsage[PoolIndex] + Amount > if (Process->QuotaUsage[QuotaType] + Amount >
Process->QuotaBlock->QuotaEntry[PoolIndex].Limit) Process->QuotaBlock->QuotaEntry[QuotaType].Limit)
{ {
DPRINT1("Quota exceeded, but ROS will let it slide...\n"); DPRINT1("Quota exceeded, but ROS will let it slide...\n");
return STATUS_SUCCESS; return STATUS_SUCCESS;
//return STATUS_QUOTA_EXCEEDED; /* caller raises the exception */ //return STATUS_QUOTA_EXCEEDED; /* caller raises the exception */
} }
InterlockedExchangeAdd((LONG*)&Process->QuotaUsage[PoolIndex], Amount); InterlockedExchangeAdd((LONG*)&Process->QuotaUsage[QuotaType], Amount);
/* Note: Race warning. TODO: Needs to add/use lock for this */ /* Note: Race warning. TODO: Needs to add/use lock for this */
if (Process->QuotaPeak[PoolIndex] < Process->QuotaUsage[PoolIndex]) if (Process->QuotaPeak[QuotaType] < Process->QuotaUsage[QuotaType])
{ {
Process->QuotaPeak[PoolIndex] = Process->QuotaUsage[PoolIndex]; Process->QuotaPeak[QuotaType] = Process->QuotaUsage[QuotaType];
} }
return STATUS_SUCCESS; return STATUS_SUCCESS;
@ -67,27 +65,25 @@ PspChargeProcessQuotaSpecifiedPool(IN PEPROCESS Process,
/* /*
* Private helper to remove quota charge from the specified process quota. * Private helper to remove quota charge from the specified process quota.
* TODO: Research and possibly add (the undocumented) enum type PS_QUOTA_TYPE
* to replace UCHAR for 'PoolIndex'.
* Notes: Conceptually translation unit local/private. * Notes: Conceptually translation unit local/private.
*/ */
VOID VOID
NTAPI NTAPI
PspReturnProcessQuotaSpecifiedPool(IN PEPROCESS Process, PspReturnProcessQuotaSpecifiedPool(IN PEPROCESS Process,
IN UCHAR PoolIndex, IN PS_QUOTA_TYPE QuotaType,
IN SIZE_T Amount) IN SIZE_T Amount)
{ {
ASSERT(Process); ASSERT(Process);
ASSERT(Process != PsInitialSystemProcess); ASSERT(Process != PsInitialSystemProcess);
ASSERT(PoolIndex <= 2); ASSERT(QuotaType < PsQuotaTypes);
ASSERT(!(Amount & 0x80000000)); /* we need to be able to negate it */ ASSERT(!(Amount & 0x80000000)); /* we need to be able to negate it */
if (Process->QuotaUsage[PoolIndex] < Amount) if (Process->QuotaUsage[QuotaType] < Amount)
{ {
DPRINT1("WARNING: Process->QuotaUsage sanity check failed.\n"); DPRINT1("WARNING: Process->QuotaUsage sanity check failed.\n");
} }
else else
{ {
InterlockedExchangeAdd((LONG*)&Process->QuotaUsage[PoolIndex], InterlockedExchangeAdd((LONG*)&Process->QuotaUsage[QuotaType],
-(LONG)Amount); -(LONG)Amount);
} }
} }
@ -100,9 +96,9 @@ NTAPI
PsInitializeQuotaSystem(VOID) PsInitializeQuotaSystem(VOID)
{ {
RtlZeroMemory(&PspDefaultQuotaBlock, sizeof(PspDefaultQuotaBlock)); RtlZeroMemory(&PspDefaultQuotaBlock, sizeof(PspDefaultQuotaBlock));
PspDefaultQuotaBlock.QuotaEntry[PagedPool].Limit = (SIZE_T)-1; PspDefaultQuotaBlock.QuotaEntry[PsNonPagedPool].Limit = (SIZE_T)-1;
PspDefaultQuotaBlock.QuotaEntry[NonPagedPool].Limit = (SIZE_T)-1; PspDefaultQuotaBlock.QuotaEntry[PsPagedPool].Limit = (SIZE_T)-1;
PspDefaultQuotaBlock.QuotaEntry[2].Limit = (SIZE_T)-1; /* Page file */ PspDefaultQuotaBlock.QuotaEntry[PsPageFile].Limit = (SIZE_T)-1;
PsGetCurrentProcess()->QuotaBlock = &PspDefaultQuotaBlock; PsGetCurrentProcess()->QuotaBlock = &PspDefaultQuotaBlock;
} }
@ -164,7 +160,7 @@ PsChargeProcessPageFileQuota(IN PEPROCESS Process,
/* Don't do anything for the system process */ /* Don't do anything for the system process */
if (Process == PsInitialSystemProcess) return STATUS_SUCCESS; if (Process == PsInitialSystemProcess) return STATUS_SUCCESS;
return PspChargeProcessQuotaSpecifiedPool(Process, 2, Amount); return PspChargeProcessQuotaSpecifiedPool(Process, PsPageFile, Amount);
} }
/* /*
@ -284,7 +280,7 @@ PsReturnProcessPageFileQuota(IN PEPROCESS Process,
/* Don't do anything for the system process */ /* Don't do anything for the system process */
if (Process == PsInitialSystemProcess) return STATUS_SUCCESS; if (Process == PsInitialSystemProcess) return STATUS_SUCCESS;
PspReturnProcessQuotaSpecifiedPool(Process, 2, Amount); PspReturnProcessQuotaSpecifiedPool(Process, PsPageFile, Amount);
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
@ -419,12 +415,12 @@ PspSetQuotaLimits(
/* Initialize the quota block */ /* Initialize the quota block */
QuotaBlock->ReferenceCount = 1; QuotaBlock->ReferenceCount = 1;
QuotaBlock->ProcessCount = 1; QuotaBlock->ProcessCount = 1;
QuotaBlock->QuotaEntry[0].Peak = Process->QuotaPeak[0]; QuotaBlock->QuotaEntry[PsNonPagedPool].Peak = Process->QuotaPeak[PsNonPagedPool];
QuotaBlock->QuotaEntry[1].Peak = Process->QuotaPeak[1]; QuotaBlock->QuotaEntry[PsPagedPool].Peak = Process->QuotaPeak[PsPagedPool];
QuotaBlock->QuotaEntry[2].Peak = Process->QuotaPeak[2]; QuotaBlock->QuotaEntry[PsPageFile].Peak = Process->QuotaPeak[PsPageFile];
QuotaBlock->QuotaEntry[0].Limit = PspDefaultQuotaBlock.QuotaEntry[0].Limit; QuotaBlock->QuotaEntry[PsNonPagedPool].Limit = PspDefaultQuotaBlock.QuotaEntry[PsNonPagedPool].Limit;
QuotaBlock->QuotaEntry[1].Limit = PspDefaultQuotaBlock.QuotaEntry[1].Limit; QuotaBlock->QuotaEntry[PsPagedPool].Limit = PspDefaultQuotaBlock.QuotaEntry[PsPagedPool].Limit;
QuotaBlock->QuotaEntry[2].Limit = PspDefaultQuotaBlock.QuotaEntry[2].Limit; QuotaBlock->QuotaEntry[PsPageFile].Limit = PspDefaultQuotaBlock.QuotaEntry[PsPageFile].Limit;
/* Try to exchange the quota block, if that failed, just drop it */ /* Try to exchange the quota block, if that failed, just drop it */
OldQuotaBlock = InterlockedCompareExchangePointer((PVOID*)&Process->QuotaBlock, OldQuotaBlock = InterlockedCompareExchangePointer((PVOID*)&Process->QuotaBlock,

View file

@ -972,6 +972,23 @@ typedef struct _JOB_SET_ARRAY
ULONG Flags; ULONG Flags;
} JOB_SET_ARRAY, *PJOB_SET_ARRAY; } JOB_SET_ARRAY, *PJOB_SET_ARRAY;
//
// Process Quota Type
//
typedef enum _PS_QUOTA_TYPE
{
PsNonPagedPool = 0,
PsPagedPool,
PsPageFile,
#if (NTDDI_VERSION >= NTDDI_LONGHORN)
PsWorkingSet,
#endif
#if (NTDDI_VERSION == NTDDI_LONGHORN)
PsCpuRate,
#endif
PsQuotaTypes
} PS_QUOTA_TYPE;
// //
// EPROCESS Quota Structures // EPROCESS Quota Structures
// //
@ -985,7 +1002,7 @@ typedef struct _EPROCESS_QUOTA_ENTRY
typedef struct _EPROCESS_QUOTA_BLOCK typedef struct _EPROCESS_QUOTA_BLOCK
{ {
EPROCESS_QUOTA_ENTRY QuotaEntry[3]; EPROCESS_QUOTA_ENTRY QuotaEntry[PsQuotaTypes];
LIST_ENTRY QuotaList; LIST_ENTRY QuotaList;
ULONG ReferenceCount; ULONG ReferenceCount;
ULONG ProcessCount; ULONG ProcessCount;
@ -1208,8 +1225,8 @@ typedef struct _EPROCESS
EX_RUNDOWN_REF RundownProtect; EX_RUNDOWN_REF RundownProtect;
HANDLE UniqueProcessId; HANDLE UniqueProcessId;
LIST_ENTRY ActiveProcessLinks; LIST_ENTRY ActiveProcessLinks;
SIZE_T QuotaUsage[3]; /* 0=PagedPool, 1=NonPagedPool, 2=Pagefile */ SIZE_T QuotaUsage[PsQuotaTypes];
SIZE_T QuotaPeak[3]; /* ditto */ SIZE_T QuotaPeak[PsQuotaTypes];
SIZE_T CommitCharge; SIZE_T CommitCharge;
SIZE_T PeakVirtualSize; SIZE_T PeakVirtualSize;
SIZE_T VirtualSize; SIZE_T VirtualSize;