From: Timo Kreuzer Date: Tue, 18 Feb 2014 18:12:30 +0000 (+0000) Subject: [NTOSKRNL] X-Git-Tag: ReactOS-0.3.16-CLT2014~90 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=9339f52e6842e3b91ecc8ecbc0ec2f3563e2e55c;hp=6a9cca1ef833d5c39b770e4e8c869f581b671f93 [NTOSKRNL] Implement PspSetQuotaLimits and use it in NtSetInformationProcess svn path=/trunk/; revision=62247 --- diff --git a/reactos/ntoskrnl/include/internal/mm.h b/reactos/ntoskrnl/include/internal/mm.h index 6a894941251..1af6e97647b 100644 --- a/reactos/ntoskrnl/include/internal/mm.h +++ b/reactos/ntoskrnl/include/internal/mm.h @@ -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) diff --git a/reactos/ntoskrnl/include/internal/ps.h b/reactos/ntoskrnl/include/internal/ps.h index af02c4557f9..67506f8d461 100644 --- a/reactos/ntoskrnl/include/internal/ps.h +++ b/reactos/ntoskrnl/include/internal/ps.h @@ -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 diff --git a/reactos/ntoskrnl/ps/query.c b/reactos/ntoskrnl/ps/query.c index 1e81a77ae63..4cc554f19cc 100644 --- a/reactos/ntoskrnl/ps/query.c +++ b/reactos/ntoskrnl/ps/query.c @@ -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"); diff --git a/reactos/ntoskrnl/ps/quota.c b/reactos/ntoskrnl/ps/quota.c index 452ed3ad082..42899ddb3db 100644 --- a/reactos/ntoskrnl/ps/quota.c +++ b/reactos/ntoskrnl/ps/quota.c @@ -11,10 +11,19 @@ /* INCLUDES **************************************************************/ #include +#include #define NDEBUG #include 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 */