mirror of
https://github.com/reactos/reactos.git
synced 2025-02-22 08:25:03 +00:00
[NTOSKRNL]
Implement PspSetQuotaLimits and use it in NtSetInformationProcess svn path=/trunk/; revision=62247
This commit is contained in:
parent
6a9cca1ef8
commit
9339f52e68
4 changed files with 228 additions and 3 deletions
|
@ -1767,6 +1767,17 @@ ExpCheckPoolAllocation(
|
|||
ULONG Tag);
|
||||
|
||||
|
||||
/* mmsup.c *****************************************************************/
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
MmAdjustWorkingSetSize(
|
||||
IN SIZE_T WorkingSetMinimumInBytes,
|
||||
IN SIZE_T WorkingSetMaximumInBytes,
|
||||
IN ULONG SystemCache,
|
||||
IN BOOLEAN IncreaseOkay);
|
||||
|
||||
|
||||
/* session.c *****************************************************************/
|
||||
|
||||
_IRQL_requires_max_(APC_LEVEL)
|
||||
|
|
|
@ -300,6 +300,15 @@ PspDestroyQuotaBlock(
|
|||
IN PEPROCESS Process
|
||||
);
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
PspSetQuotaLimits(
|
||||
_In_ HANDLE ProcessHandle,
|
||||
_In_ ULONG Unused,
|
||||
_In_ PVOID QuotaLimits,
|
||||
_In_ ULONG QuotaLimitsLength,
|
||||
_In_ KPROCESSOR_MODE PreviousMode);
|
||||
|
||||
#if defined(_X86_)
|
||||
//
|
||||
// VDM and LDT Support
|
||||
|
|
|
@ -1917,9 +1917,12 @@ NtSetInformationProcess(IN HANDLE ProcessHandle,
|
|||
break;
|
||||
|
||||
case ProcessQuotaLimits:
|
||||
DPRINT1("Quota Limits not implemented\n");
|
||||
Status = STATUS_NOT_IMPLEMENTED;
|
||||
break;
|
||||
|
||||
return PspSetQuotaLimits(ProcessHandle,
|
||||
1,
|
||||
ProcessInformation,
|
||||
ProcessInformationLength,
|
||||
PreviousMode);
|
||||
|
||||
case ProcessWorkingSetWatch:
|
||||
DPRINT1("WS watch not implemented\n");
|
||||
|
|
|
@ -11,10 +11,19 @@
|
|||
/* INCLUDES **************************************************************/
|
||||
|
||||
#include <ntoskrnl.h>
|
||||
#include <ntintsafe.h>
|
||||
#define NDEBUG
|
||||
#include <debug.h>
|
||||
|
||||
EPROCESS_QUOTA_BLOCK PspDefaultQuotaBlock;
|
||||
static LIST_ENTRY PspQuotaBlockList = {&PspQuotaBlockList, &PspQuotaBlockList};
|
||||
static KSPIN_LOCK PspQuotaLock;
|
||||
|
||||
#define TAG_QUOTA_BLOCK 'bQsP'
|
||||
#define VALID_QUOTA_FLAGS (QUOTA_LIMITS_HARDWS_MIN_ENABLE | \
|
||||
QUOTA_LIMITS_HARDWS_MIN_DISABLE | \
|
||||
QUOTA_LIMITS_HARDWS_MAX_ENABLE | \
|
||||
QUOTA_LIMITS_HARDWS_MAX_DISABLE)
|
||||
|
||||
/* PRIVATE FUNCTIONS *******************************************************/
|
||||
|
||||
|
@ -116,15 +125,31 @@ PspInheritQuota(PEPROCESS Process, PEPROCESS ParentProcess)
|
|||
Process->QuotaBlock = &PspDefaultQuotaBlock;
|
||||
}
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
PspInsertQuotaBlock(
|
||||
PEPROCESS_QUOTA_BLOCK QuotaBlock)
|
||||
{
|
||||
KIRQL OldIrql;
|
||||
|
||||
OldIrql = KfAcquireSpinLock(&PspQuotaLock);
|
||||
InsertTailList(&PspQuotaBlockList, &QuotaBlock->QuotaList);
|
||||
KfReleaseSpinLock(&PspQuotaLock, OldIrql);
|
||||
}
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
PspDestroyQuotaBlock(PEPROCESS Process)
|
||||
{
|
||||
PEPROCESS_QUOTA_BLOCK QuotaBlock = Process->QuotaBlock;
|
||||
KIRQL OldIrql;
|
||||
|
||||
if (QuotaBlock != &PspDefaultQuotaBlock &&
|
||||
InterlockedDecrementUL(&QuotaBlock->ReferenceCount) == 0)
|
||||
{
|
||||
OldIrql = KfAcquireSpinLock(&PspQuotaLock);
|
||||
RemoveEntryList(&QuotaBlock->QuotaList);
|
||||
KfReleaseSpinLock(&PspQuotaLock, OldIrql);
|
||||
ExFreePool(QuotaBlock);
|
||||
}
|
||||
}
|
||||
|
@ -264,4 +289,181 @@ PsReturnProcessPageFileQuota(IN PEPROCESS Process,
|
|||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
PspSetQuotaLimits(
|
||||
_In_ HANDLE ProcessHandle,
|
||||
_In_ ULONG Unused,
|
||||
_In_ PVOID QuotaLimits,
|
||||
_In_ ULONG QuotaLimitsLength,
|
||||
_In_ KPROCESSOR_MODE PreviousMode)
|
||||
{
|
||||
QUOTA_LIMITS_EX CapturedQuotaLimits;
|
||||
PEPROCESS Process;
|
||||
PEPROCESS_QUOTA_BLOCK QuotaBlock, OldQuotaBlock;
|
||||
BOOLEAN IncreaseOkay;
|
||||
KAPC_STATE SavedApcState;
|
||||
NTSTATUS Status;
|
||||
|
||||
UNREFERENCED_PARAMETER(Unused);
|
||||
|
||||
_SEH2_TRY
|
||||
{
|
||||
ProbeForRead(QuotaLimits, QuotaLimitsLength, sizeof(ULONG));
|
||||
|
||||
/* Check if we have the basic or extended structure */
|
||||
if (QuotaLimitsLength == sizeof(QUOTA_LIMITS))
|
||||
{
|
||||
/* Copy the basic structure, zero init the remaining fields */
|
||||
RtlCopyMemory(&CapturedQuotaLimits, QuotaLimits, sizeof(QUOTA_LIMITS));
|
||||
CapturedQuotaLimits.WorkingSetLimit = 0;
|
||||
CapturedQuotaLimits.Reserved2 = 0;
|
||||
CapturedQuotaLimits.Reserved3 = 0;
|
||||
CapturedQuotaLimits.Reserved4 = 0;
|
||||
CapturedQuotaLimits.CpuRateLimit.RateData = 0;
|
||||
CapturedQuotaLimits.Flags = 0;
|
||||
}
|
||||
else if (QuotaLimitsLength == sizeof(QUOTA_LIMITS_EX))
|
||||
{
|
||||
/* Copy the full structure */
|
||||
RtlCopyMemory(&CapturedQuotaLimits, QuotaLimits, sizeof(QUOTA_LIMITS_EX));
|
||||
|
||||
/* Verify that the caller passed valid flags */
|
||||
if ((CapturedQuotaLimits.Flags & ~VALID_QUOTA_FLAGS) ||
|
||||
((CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MIN_ENABLE) &&
|
||||
(CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MIN_DISABLE)) ||
|
||||
((CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MAX_ENABLE) &&
|
||||
(CapturedQuotaLimits.Flags & QUOTA_LIMITS_HARDWS_MAX_DISABLE)))
|
||||
{
|
||||
DPRINT1("Invalid quota flags: 0x%lx\n", CapturedQuotaLimits.Flags);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Verify that the caller didn't pass reserved values */
|
||||
if ((CapturedQuotaLimits.WorkingSetLimit != 0) ||
|
||||
(CapturedQuotaLimits.Reserved2 != 0) ||
|
||||
(CapturedQuotaLimits.Reserved3 != 0) ||
|
||||
(CapturedQuotaLimits.Reserved4 != 0) ||
|
||||
(CapturedQuotaLimits.CpuRateLimit.RateData != 0))
|
||||
{
|
||||
DPRINT1("Invalid value: (%lx,%lx,%lx,%lx,%lx)\n",
|
||||
CapturedQuotaLimits.WorkingSetLimit,
|
||||
CapturedQuotaLimits.Reserved2,
|
||||
CapturedQuotaLimits.Reserved3,
|
||||
CapturedQuotaLimits.Reserved4,
|
||||
CapturedQuotaLimits.CpuRateLimit.RateData);
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINT1("Invalid quota size: 0x%lx\n", QuotaLimitsLength);
|
||||
return STATUS_INFO_LENGTH_MISMATCH;
|
||||
}
|
||||
}
|
||||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
DPRINT1("Exception while copying data\n");
|
||||
return _SEH2_GetExceptionCode();
|
||||
}
|
||||
_SEH2_END;
|
||||
|
||||
/* Reference the process */
|
||||
Status = ObReferenceObjectByHandle(ProcessHandle,
|
||||
PROCESS_SET_QUOTA,
|
||||
PsProcessType,
|
||||
PreviousMode,
|
||||
(PVOID*)&Process,
|
||||
NULL);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("Failed to reference process handle: 0x%lx\n", Status);
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Check the caller changes the working set size limits */
|
||||
if ((CapturedQuotaLimits.MinimumWorkingSetSize != 0) &&
|
||||
(CapturedQuotaLimits.MaximumWorkingSetSize != 0))
|
||||
{
|
||||
/* Check for special case: trimming the WS */
|
||||
if ((CapturedQuotaLimits.MinimumWorkingSetSize == SIZE_T_MAX) &&
|
||||
(CapturedQuotaLimits.MaximumWorkingSetSize == SIZE_T_MAX))
|
||||
{
|
||||
/* No increase allowed */
|
||||
IncreaseOkay = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check if the caller has the required privilege */
|
||||
IncreaseOkay = SeSinglePrivilegeCheck(SeIncreaseQuotaPrivilege,
|
||||
PreviousMode);
|
||||
}
|
||||
|
||||
/* Attach to the target process and disable APCs */
|
||||
KeStackAttachProcess(&Process->Pcb, &SavedApcState);
|
||||
KeEnterGuardedRegion();
|
||||
|
||||
/* Call Mm to adjust the process' working set size */
|
||||
Status = MmAdjustWorkingSetSize(CapturedQuotaLimits.MinimumWorkingSetSize,
|
||||
CapturedQuotaLimits.MaximumWorkingSetSize,
|
||||
0,
|
||||
IncreaseOkay);
|
||||
|
||||
/* Bring back APCs and detach from the process */
|
||||
KeLeaveGuardedRegion();
|
||||
KeUnstackDetachProcess(&SavedApcState);
|
||||
}
|
||||
else if (Process->QuotaBlock == &PspDefaultQuotaBlock)
|
||||
{
|
||||
/* Check if the caller has the required privilege */
|
||||
if (!SeSinglePrivilegeCheck(SeIncreaseQuotaPrivilege, PreviousMode))
|
||||
{
|
||||
ObDereferenceObject(Process);
|
||||
return STATUS_PRIVILEGE_NOT_HELD;
|
||||
}
|
||||
|
||||
/* Allocate a new quota block */
|
||||
QuotaBlock = ExAllocatePoolWithTag(NonPagedPool,
|
||||
sizeof(EPROCESS_QUOTA_BLOCK),
|
||||
TAG_QUOTA_BLOCK);
|
||||
if (QuotaBlock == NULL)
|
||||
{
|
||||
ObDereferenceObject(Process);
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* Initialize the quota block */
|
||||
QuotaBlock->ReferenceCount = 1;
|
||||
QuotaBlock->ProcessCount = 1;
|
||||
QuotaBlock->QuotaEntry[0].Peak = Process->QuotaPeak[0];
|
||||
QuotaBlock->QuotaEntry[1].Peak = Process->QuotaPeak[1];
|
||||
QuotaBlock->QuotaEntry[2].Peak = Process->QuotaPeak[2];
|
||||
QuotaBlock->QuotaEntry[0].Limit = PspDefaultQuotaBlock.QuotaEntry[0].Limit;
|
||||
QuotaBlock->QuotaEntry[1].Limit = PspDefaultQuotaBlock.QuotaEntry[1].Limit;
|
||||
QuotaBlock->QuotaEntry[2].Limit = PspDefaultQuotaBlock.QuotaEntry[2].Limit;
|
||||
|
||||
/* Try to exchange the quota block, if that failed, just drop it */
|
||||
OldQuotaBlock = InterlockedCompareExchangePointer(&Process->QuotaBlock,
|
||||
QuotaBlock,
|
||||
&PspDefaultQuotaBlock);
|
||||
if (OldQuotaBlock == &PspDefaultQuotaBlock)
|
||||
{
|
||||
/* Success, insert the new quota block */
|
||||
PspInsertQuotaBlock(QuotaBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Failed, free the quota block and ignore it */
|
||||
ExFreePoolWithTag(QuotaBlock, TAG_QUOTA_BLOCK);
|
||||
}
|
||||
|
||||
Status = STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Dereference the process and return the status */
|
||||
ObDereferenceObject(Process);
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
||||
/* EOF */
|
||||
|
|
Loading…
Reference in a new issue