diff --git a/xtoskrnl/CMakeLists.txt b/xtoskrnl/CMakeLists.txt index 5025097..526bc3c 100644 --- a/xtoskrnl/CMakeLists.txt +++ b/xtoskrnl/CMakeLists.txt @@ -89,7 +89,9 @@ list(APPEND XTOSKRNL_SOURCE ${XTOSKRNL_SOURCE_DIR}/mm/pte.cc ${XTOSKRNL_SOURCE_DIR}/mm/quota.cc ${XTOSKRNL_SOURCE_DIR}/po/idle.cc + ${XTOSKRNL_SOURCE_DIR}/ps/data.cc ${XTOSKRNL_SOURCE_DIR}/ps/process.cc + ${XTOSKRNL_SOURCE_DIR}/ps/quota.cc ${XTOSKRNL_SOURCE_DIR}/ps/thread.cc ${XTOSKRNL_SOURCE_DIR}/rtl/${ARCH}/atomic.cc ${XTOSKRNL_SOURCE_DIR}/rtl/${ARCH}/dispatch.cc diff --git a/xtoskrnl/includes/ps.hh b/xtoskrnl/includes/ps.hh index c072314..9218771 100644 --- a/xtoskrnl/includes/ps.hh +++ b/xtoskrnl/includes/ps.hh @@ -12,6 +12,7 @@ #include #include +#include #include diff --git a/xtoskrnl/includes/ps/quota.hh b/xtoskrnl/includes/ps/quota.hh new file mode 100644 index 0000000..a97ac62 --- /dev/null +++ b/xtoskrnl/includes/ps/quota.hh @@ -0,0 +1,38 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/includes/ps/quota.hh + * DESCRIPTION: Process Quota Management + * DEVELOPERS: Aiken Harris + */ + +#ifndef __XTOSKRNL_PS_QUOTA_HH +#define __XTOSKRNL_PS_QUOTA_HH + +#include + + +/* Process and thread management */ +namespace PS +{ + class Quota + { + private: + STATIC EPROCESS_QUOTA_BLOCK DefaultQuotaBlock; + + public: + STATIC XTAPI VOID ReturnSharedPoolQuota(IN PEPROCESS_QUOTA_BLOCK QuotaBlock, + IN SIZE_T PagedAmount, + IN SIZE_T NonPagedAmount); + + private: + STATIC XTAPI VOID DereferenceQuotaBlock(IN PEPROCESS_QUOTA_BLOCK QuotaBlock); + STATIC XTFASTCALL VOID ReturnProcessQuota(IN PEPROCESS_QUOTA_BLOCK QuotaBlock, + IN PEPROCESS Process, + IN PS_QUOTA_TYPE QuotaType, + IN SIZE_T Amount); + + }; +} + +#endif /* __XTOSKRNL_PS_QUOTA_HH */ diff --git a/xtoskrnl/ps/data.cc b/xtoskrnl/ps/data.cc new file mode 100644 index 0000000..bd3d092 --- /dev/null +++ b/xtoskrnl/ps/data.cc @@ -0,0 +1,13 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/po/data.cc + * DESCRIPTION: Process global and static data + * DEVELOPERS: Aiken Harris + */ + +#include + + +/* Default system-wide quota block assigned to processes */ +EPROCESS_QUOTA_BLOCK PS::Quota::DefaultQuotaBlock; diff --git a/xtoskrnl/ps/quota.cc b/xtoskrnl/ps/quota.cc new file mode 100644 index 0000000..bd6b063 --- /dev/null +++ b/xtoskrnl/ps/quota.cc @@ -0,0 +1,211 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/po/quota.cc + * DESCRIPTION: Process Quota Management + * DEVELOPERS: Aiken Harris + */ + +#include + + +/** + * Decrements the reference count of a quota block and destroys it if it reaches zero. + * + * @param QuotaBlock + * Supplies a pointer to the process quota block to dereference. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +PS::Quota::DereferenceQuotaBlock(IN PEPROCESS_QUOTA_BLOCK QuotaBlock) +{ + SIZE_T ReturnAmount; + ULONG Index; + + /* Guard against null pointer and the bypass marker */ + if(QuotaBlock == NULLPTR || QuotaBlock == PS_QUOTA_BYPASS_MARKER) + { + /* No cleanup is required, return */ + return; + } + + /* Decrement the reference count */ + if(RTL::Atomic::Decrement32((VOLATILE PLONG)&QuotaBlock->ReferenceCount) == 0) + { + /* Start a guarded code block */ + { + /* Raise runlevel to DISPATCH level */ + KE::RaiseRunLevel RunLevel(DISPATCH_LEVEL); + + /* Iterate through all available pool quota types */ + for(Index = PsNonPagedPool; Index <= PsPagedPool; Index++) + { + /* Calculate the total residual quota */ + ReturnAmount = QuotaBlock->QuotaEntry[Index].Return + QuotaBlock->QuotaEntry[Index].Limit; + + /* Check if there is any residual quota left */ + if(ReturnAmount > 0) + { + /* Return the residual quota */ + MM::Quota::ReturnPoolQuota((MMPOOL_TYPE)Index, ReturnAmount); + } + } + } + + /* Unlink the quota block from the global tracking list */ + RTL::LinkedList::RemoveEntryList(&QuotaBlock->QuotaList); + + /* Free the physical memory */ + MM::Allocator::FreePool(QuotaBlock, TAG_PS_QUOTA_BLOCK); + } +} + +/** + * Returns a specified amount of quota to a quota block and updates process usage. + * + * @param QuotaBlock + * Supplies the quota block receiving the returned quota. + * + * @param Process + * Supplies an optional pointer to the process whose usage is being reduced. + * + * @param QuotaType + * Supplies the type of quota being returned. + * + * @param Amount + * Supplies the amount of quota to return. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTFASTCALL +VOID +PS::Quota::ReturnProcessQuota(IN PEPROCESS_QUOTA_BLOCK QuotaBlock, + IN PEPROCESS Process, + IN PS_QUOTA_TYPE QuotaType, + IN SIZE_T Amount) +{ + SIZE_T AccumulatedGiveBack, ExtractedReturnAmount, GiveBackAmount, GiveBackLimit, MinGiveBackThreshold; + SIZE_T CurrentLimit, CurrentUsage, ExpectedLimit, NewLimit; + PEPROCESS_QUOTA_ENTRY QuotaEntry; + + /* Initialize local pointers and fetch the current usage for the specified quota type */ + QuotaEntry = &QuotaBlock->QuotaEntry[QuotaType]; + CurrentUsage = QuotaEntry->Usage; + + /* Ensure quota usage does not exceed the limit */ + if((Process != NULLPTR && Process->QuotaUsage[QuotaType] < Amount) || (CurrentUsage < Amount)) + { + /* Quota underflow detected, raise kernel panic */ + KE::Crash::Panic(0x21, + (ULONG_PTR)Process, + (ULONG_PTR)QuotaType, + Process ? (ULONG_PTR)Process->QuotaUsage[QuotaType] : (ULONG_PTR)CurrentUsage, + (ULONG_PTR)Amount); + } + + /* Determine the thresholds for returning excess quota limit */ + CurrentLimit = QuotaEntry->Limit; + MinGiveBackThreshold = (MMPAGED_QUOTA_INCREASE > MMNONPAGED_QUOTA_INCREASE) ? MMNONPAGED_QUOTA_INCREASE + : MMPAGED_QUOTA_INCREASE; + + /* Check if the current limit exceeds usage by at least the minimum giveback threshold */ + if((CurrentLimit > CurrentUsage) && ((CurrentLimit - CurrentUsage) > MinGiveBackThreshold)) + { + /* Only proceed if this is a pool quota */ + if(QuotaType != PsPageFile && QuotaBlock != &DefaultQuotaBlock) + { + /* Calculate the exact amount of limit to trim based on pool type block size */ + GiveBackLimit = (QuotaType == PsPagedPool) ? MMPAGED_QUOTA_INCREASE : MMNONPAGED_QUOTA_INCREASE; + GiveBackAmount = (GiveBackLimit > Amount) ? Amount : GiveBackLimit; + + /* Attempt a lock-free update to shrink the quota limit */ + NewLimit = CurrentLimit - GiveBackAmount; + ExpectedLimit = (SIZE_T)RTL::Atomic::CompareExchangePointer((PVOID*)&QuotaEntry->Limit, (PVOID)NewLimit, + (PVOID)CurrentLimit); + + /* Check if the atomic limit update succeeded */ + if(ExpectedLimit == CurrentLimit) + { + /* Add to the return counter */ + AccumulatedGiveBack = RTL::Atomic::Add64((PLONG_PTR)&QuotaEntry->Return, GiveBackAmount); + + /* Check if the accumulated returns exceed the threshold */ + if(AccumulatedGiveBack > GiveBackLimit) + { + /* Extract the accumulated return amount and reset the counter to zero */ + ExtractedReturnAmount = (SIZE_T)RTL::Atomic::ExchangePointer((PVOID*)&QuotaEntry->Return, (PVOID)0); + + /* Check if there is anything to return */ + if(ExtractedReturnAmount > 0) + { + /* Return the extracted quota back to the system pool */ + MM::Quota::ReturnPoolQuota((MMPOOL_TYPE)QuotaType, ExtractedReturnAmount); + } + } + } + } + } + + /* Decrease the block's overall usage count */ + RTL::Atomic::Add64((PLONG_PTR)&QuotaEntry->Usage, (SIZE_T)(-(SSIZE_T)Amount)); + + /* Check if a specific process was provided */ + if(Process != NULLPTR) + { + /* Decrease the process's specific usage count */ + RTL::Atomic::Add64((PLONG_PTR)&Process->QuotaUsage[QuotaType], (SIZE_T)(-(SSIZE_T)Amount)); + } +} + +/** + * Returns previously charged pool amounts back to a shared quota block. + * + * @param QuotaBlock + * Supplies a pointer to the process quota block. + * + * @param PagedAmount + * Supplies the amount of paged pool quota to return, in bytes. + * + * @param NonPagedAmount + * Supplies the amount of non-paged pool quota to return, in bytes. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +PS::Quota::ReturnSharedPoolQuota(IN PEPROCESS_QUOTA_BLOCK QuotaBlock, + IN SIZE_T PagedAmount, + IN SIZE_T NonPagedAmount) +{ + /* Check for NULL pointers and bypass markers */ + if(QuotaBlock == NULLPTR || QuotaBlock == (PEPROCESS_QUOTA_BLOCK)1) + { + /* No quota tracking is required for the system process or empty blocks, return */ + return; + } + + /* Check for paged pool quota */ + if(PagedAmount > 0) + { + /* Process the return of paged pool memory */ + ReturnProcessQuota(QuotaBlock, NULLPTR, PsPagedPool, PagedAmount); + } + + /* Check for non-paged pool quota */ + if(NonPagedAmount > 0) + { + /* Process the return of non-paged pool memory */ + ReturnProcessQuota(QuotaBlock, NULLPTR, PsNonPagedPool, NonPagedAmount); + } + + /* Drop the reference count on the quota block */ + DereferenceQuotaBlock(QuotaBlock); +}