From 2f2139d851f5ea9b5263870e6a8beaec19dbf929 Mon Sep 17 00:00:00 2001 From: Alex Ionescu Date: Fri, 21 Jul 2006 19:28:38 +0000 Subject: [PATCH] - Implement Kernel, Memory Manager and Process Manager APIs for the following concepts (not yet used): - Process Quantum. - Fixed and Variable Quantum Tables. - Long and Short Quantum Variability. - Priority Separation and Separation Masks. - Foreground Quantum Table. - Process Priority, Priority Modes and Priority Classes. - Virtual Memory Priority - Job Scheduling Classes - Implement PsSetProcessPriorityByClass. svn path=/trunk/; revision=23214 --- reactos/include/ndk/ketypes.h | 3 + reactos/include/ndk/pstypes.h | 13 ++ reactos/ntoskrnl/KrnlFun.c | 3 +- reactos/ntoskrnl/include/internal/ke.h | 15 ++ reactos/ntoskrnl/include/internal/mm.h | 7 + reactos/ntoskrnl/include/internal/ps.h | 5 +- reactos/ntoskrnl/include/internal/ps_x.h | 12 ++ reactos/ntoskrnl/ke/process.c | 198 +++++++++++++++++++++ reactos/ntoskrnl/mm/mminit.c | 2 +- reactos/ntoskrnl/mm/process.c | 24 +++ reactos/ntoskrnl/ps/job.c | 18 +- reactos/ntoskrnl/ps/process.c | 217 ++++++++++++++++++++++- 12 files changed, 507 insertions(+), 10 deletions(-) diff --git a/reactos/include/ndk/ketypes.h b/reactos/include/ndk/ketypes.h index cff284123d4..535b9a4f447 100644 --- a/reactos/include/ndk/ketypes.h +++ b/reactos/include/ndk/ketypes.h @@ -929,6 +929,9 @@ typedef struct _KPROCESS #endif } KPROCESS, *PKPROCESS; +#define ASSERT_PROCESS(object) \ + ASSERT((((object)->Header.Type & KOBJECT_TYPE_MASK) == ProcessObject)) + // // System Service Table Descriptor // diff --git a/reactos/include/ndk/pstypes.h b/reactos/include/ndk/pstypes.h index b17f0f6bd5f..d6f8d6ffc29 100644 --- a/reactos/include/ndk/pstypes.h +++ b/reactos/include/ndk/pstypes.h @@ -108,6 +108,12 @@ extern NTSYSAPI POBJECT_TYPE PsProcessType; #define PROCESS_PRIORITY_NORMAL 8 #define PROCESS_PRIORITY_NORMAL_FOREGROUND 9 +// +// Process Priority Separation Values (OR) +// +#define PSP_VARIABLE_QUANTUMS 4 +#define PSP_LONG_QUANTUMS 16 + // // Number of TLS expansion slots // @@ -304,6 +310,13 @@ typedef enum _THREADINFOCLASS #else +typedef enum _PSPROCESSPRIORITYMODE +{ + PsProcessPriorityForeground, + PsProcessPriorityBackground, + PsProcessPrioritySpinning +} PSPROCESSPRIORITYMODE; + typedef enum _JOBOBJECTINFOCLASS { JobObjectBasicAccountingInformation = 1, diff --git a/reactos/ntoskrnl/KrnlFun.c b/reactos/ntoskrnl/KrnlFun.c index 948c6843342..98949545e8f 100644 --- a/reactos/ntoskrnl/KrnlFun.c +++ b/reactos/ntoskrnl/KrnlFun.c @@ -48,4 +48,5 @@ // Ke: // - Add code for interval recalulation when wait interrupted by an APC // -/////////////////////////////////////////////////////////////////////////////// \ No newline at end of file +/////////////////////////////////////////////////////////////////////////////// + diff --git a/reactos/ntoskrnl/include/internal/ke.h b/reactos/ntoskrnl/include/internal/ke.h index 5a657eaf0d7..7a61aff1edd 100644 --- a/reactos/ntoskrnl/include/internal/ke.h +++ b/reactos/ntoskrnl/include/internal/ke.h @@ -397,6 +397,21 @@ KeInitializeProcess( LARGE_INTEGER DirectoryTableBase ); +VOID +NTAPI +KeSetQuantumProcess( + IN PKPROCESS Process, + IN UCHAR Quantum +); + +KPRIORITY +NTAPI +KeSetPriorityAndQuantumProcess( + IN PKPROCESS Process, + IN KPRIORITY Priority, + IN UCHAR Quantum OPTIONAL +); + ULONG STDCALL KeForceResumeThread(IN PKTHREAD Thread); diff --git a/reactos/ntoskrnl/include/internal/mm.h b/reactos/ntoskrnl/include/internal/mm.h index 3f6a3050f36..18b90306b1b 100644 --- a/reactos/ntoskrnl/include/internal/mm.h +++ b/reactos/ntoskrnl/include/internal/mm.h @@ -654,6 +654,13 @@ ULONG NTAPI MmGetSessionLocaleId(VOID); +NTSTATUS +NTAPI +MmSetMemoryPriorityProcess( + IN PEPROCESS Process, + IN UCHAR MemoryPriority +); + /* i386/pfault.c *************************************************************/ NTSTATUS diff --git a/reactos/ntoskrnl/include/internal/ps.h b/reactos/ntoskrnl/include/internal/ps.h index 5f0b2d6f7c6..7e1f0336f06 100644 --- a/reactos/ntoskrnl/include/internal/ps.h +++ b/reactos/ntoskrnl/include/internal/ps.h @@ -19,6 +19,8 @@ struct _EJOB; #define PSP_MAX_LOAD_IMAGE_NOTIFY 8 #define PSP_MAX_CREATE_PROCESS_NOTIFY 8 +#define PSP_JOB_SCHEDULING_CLASSES 10 + VOID NTAPI PspShutdownProcessManager(VOID); @@ -226,7 +228,8 @@ extern PKWIN32_PROCESS_CALLOUT PspW32ProcessCallout; extern PKWIN32_THREAD_CALLOUT PspW32ThreadCallout; extern PVOID PspSystemDllEntryPoint; extern PVOID PspSystemDllBase; - +extern BOOLEAN PspUseJobSchedulingClasses; +extern CHAR PspJobSchedulingClasses[PSP_JOB_SCHEDULING_CLASSES]; #include "ps_x.h" #endif /* __INCLUDE_INTERNAL_PS_H */ diff --git a/reactos/ntoskrnl/include/internal/ps_x.h b/reactos/ntoskrnl/include/internal/ps_x.h index 8f535b1d2ee..ca91e583a10 100644 --- a/reactos/ntoskrnl/include/internal/ps_x.h +++ b/reactos/ntoskrnl/include/internal/ps_x.h @@ -7,6 +7,18 @@ * Thomas Weidenmueller (w3seek@reactos.org) */ +// +// Extract Quantum Settings from the Priority Separation Mask +// +#define PspPrioritySeparationFromMask(Mask) \ + ((Mask) & 3) + +#define PspQuantumTypeFromMask(Mask) \ + ((Mask) & 12) + +#define PspQuantumLengthFromMask(Mask) \ + ((Mask) & 48) + VOID FORCEINLINE PspRunCreateThreadNotifyRoutines(IN PETHREAD CurrentThread, diff --git a/reactos/ntoskrnl/ke/process.c b/reactos/ntoskrnl/ke/process.c index eacb5fcf383..e619a104b5a 100644 --- a/reactos/ntoskrnl/ke/process.c +++ b/reactos/ntoskrnl/ke/process.c @@ -221,6 +221,204 @@ KiSwapProcess(PKPROCESS NewProcess, Ke386SetPageTableDirectory(NewProcess->DirectoryTableBase.u.LowPart); } +VOID +NTAPI +KeSetQuantumProcess(IN PKPROCESS Process, + IN UCHAR Quantum) +{ + KIRQL OldIrql; + PLIST_ENTRY NextEntry, ListHead; + PKTHREAD Thread; + ASSERT_PROCESS(Process); + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* Lock Dispatcher */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Set new quantum */ + Process->QuantumReset = Quantum; + + /* Loop all child threads */ + ListHead = &Process->ThreadListHead; + NextEntry = ListHead->Flink; + while (ListHead != NextEntry) + { + /* Get the thread */ + Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry); + + /* Set quantum */ + Thread->QuantumReset = Quantum; + + /* Go to the next one */ + NextEntry = NextEntry->Flink; + } + + /* Release Dispatcher Database */ + KeReleaseDispatcherDatabaseLock(OldIrql); +} + +KPRIORITY +NTAPI +KeSetPriorityAndQuantumProcess(IN PKPROCESS Process, + IN KPRIORITY Priority, + IN UCHAR Quantum OPTIONAL) +{ + KPRIORITY Delta; + PLIST_ENTRY NextEntry, ListHead; + KPRIORITY NewPriority, OldPriority; + KIRQL OldIrql; + PKTHREAD Thread; + BOOLEAN Released; + ASSERT_PROCESS(Process); + ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); + + /* Check if the process already has this priority */ + if (Process->BasePriority == Priority) + { + /* Don't change anything */ + return Process->BasePriority; + } + + /* If the caller gave priority 0, normalize to 1 */ + if (!Priority) Priority = 1; + + /* Lock Dispatcher */ + OldIrql = KeAcquireDispatcherDatabaseLock(); + + /* Check if we are modifying the quantum too */ + if (Quantum) Process->QuantumReset = Quantum; + + /* Save the current base priority and update it */ + OldPriority = Process->BasePriority; + Process->BasePriority = Priority; + + /* Calculate the priority delta */ + Delta = Priority - OldPriority; + + /* Set the list head and list entry */ + ListHead = &Process->ThreadListHead; + NextEntry = ListHead->Flink; + + /* Check if this is a real-time priority */ + if (Priority >= LOW_REALTIME_PRIORITY) + { + /* Loop the thread list */ + while (NextEntry != ListHead) + { + /* Get the thread */ + Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry); + + /* Update the quantum if we had one */ + if (Quantum) Thread->QuantumReset = Quantum; + + /* Calculate the new priority */ + NewPriority = Thread->BasePriority + Delta; + if (NewPriority < LOW_REALTIME_PRIORITY) + { + /* We're in real-time range, don't let it go below */ + NewPriority = LOW_REALTIME_PRIORITY; + } + else if (NewPriority > HIGH_PRIORITY) + { + /* We're going beyond the maximum priority, normalize */ + NewPriority = HIGH_PRIORITY; + } + + /* + * If priority saturation occured or the old priority was still in + * the real-time range, don't do anything. + */ + if (!(Thread->Saturation) || (OldPriority < LOW_REALTIME_PRIORITY)) + { + /* Check if we had priority saturation */ + if (Thread->Saturation > 0) + { + /* Boost priority to maximum */ + NewPriority = HIGH_PRIORITY; + } + else if (Thread->Saturation < 0) + { + /* If we had negative saturation, set minimum priority */ + NewPriority = LOW_REALTIME_PRIORITY; + } + + /* Update priority and quantum */ + Thread->BasePriority = NewPriority; + Thread->Quantum = Thread->QuantumReset; + + /* Disable decrements and update priority */ + Thread->PriorityDecrement = 0; + KiSetPriorityThread(Thread, NewPriority, &Released); + } + + /* Go to the next thread */ + NextEntry = NextEntry->Flink; + } + } + else + { + /* Loop the thread list */ + while (NextEntry != ListHead) + { + /* Get the thread */ + Thread = CONTAINING_RECORD(NextEntry, KTHREAD, ThreadListEntry); + + /* Update the quantum if we had one */ + if (Quantum) Thread->QuantumReset = Quantum; + + /* Calculate the new priority */ + NewPriority = Thread->BasePriority + Delta; + if (NewPriority >= LOW_REALTIME_PRIORITY) + { + /* We're not real-time range, don't let it enter RT range */ + NewPriority = LOW_REALTIME_PRIORITY - 1; + } + else if (NewPriority <= LOW_PRIORITY) + { + /* We're going below the minimum priority, normalize */ + NewPriority = 1; + } + + /* + * If priority saturation occured or the old priority was still in + * the real-time range, don't do anything. + */ + if (!(Thread->Saturation) || + (OldPriority >= LOW_REALTIME_PRIORITY)) + { + /* Check if we had priority saturation */ + if (Thread->Saturation > 0) + { + /* Boost priority to maximum */ + NewPriority = LOW_REALTIME_PRIORITY - 1; + } + else if (Thread->Saturation < 0) + { + /* If we had negative saturation, set minimum priority */ + NewPriority = 1; + } + + /* Update priority and quantum */ + Thread->BasePriority = NewPriority; + Thread->Quantum = Thread->QuantumReset; + + /* Disable decrements and update priority */ + Thread->PriorityDecrement = 0; + KiSetPriorityThread(Thread, NewPriority, &Released); + } + + /* Go to the next thread */ + NextEntry = NextEntry->Flink; + } + } + + /* Release Dispatcher Database */ + if (!Released) KeReleaseDispatcherDatabaseLock(OldIrql); + + /* Return previous priority */ + return OldPriority; +} + /* * @implemented */ diff --git a/reactos/ntoskrnl/mm/mminit.c b/reactos/ntoskrnl/mm/mminit.c index e99c6aa6a38..a1d94ab2d3d 100644 --- a/reactos/ntoskrnl/mm/mminit.c +++ b/reactos/ntoskrnl/mm/mminit.c @@ -29,7 +29,7 @@ extern unsigned int _bss_end__; static BOOLEAN IsThisAnNtAsSystem = FALSE; -static MM_SYSTEM_SIZE MmSystemSize = MmSmallSystem; +MM_SYSTEM_SIZE MmSystemSize = MmSmallSystem; PHYSICAL_ADDRESS MmSharedDataPagePhysicalAddress; diff --git a/reactos/ntoskrnl/mm/process.c b/reactos/ntoskrnl/mm/process.c index 7b54aa9833e..bd8c46a310b 100644 --- a/reactos/ntoskrnl/mm/process.c +++ b/reactos/ntoskrnl/mm/process.c @@ -17,12 +17,36 @@ extern ULONG NtMajorVersion; extern ULONG NtMinorVersion; extern ULONG NtOSCSDVersion; extern ULONG NtGlobalFlag; +extern MM_SYSTEM_SIZE MmSystemSize; #define MM_HIGHEST_VAD_ADDRESS \ (PVOID)((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (16 * PAGE_SIZE)) /* FUNCTIONS *****************************************************************/ +NTSTATUS +NTAPI +MmSetMemoryPriorityProcess(IN PEPROCESS Process, + IN UCHAR MemoryPriority) +{ + UCHAR OldPriority; + + /* Check if we have less then 16MB of Physical Memory */ + if ((MmSystemSize == MmSmallSystem) && + (MmStats.NrTotalPages < ((15 * 1024 * 1024) / PAGE_SIZE))) + { + /* Always use background priority */ + MemoryPriority = 0; + } + + /* Save the old priority and update it */ + OldPriority = Process->Vm.Flags.MemoryPriority; + Process->Vm.Flags.MemoryPriority = MemoryPriority; + + /* Return the old priority */ + return OldPriority; +} + LCID NTAPI MmGetSessionLocaleId(VOID) diff --git a/reactos/ntoskrnl/ps/job.c b/reactos/ntoskrnl/ps/job.c index f4112b58b86..25125b9ade1 100644 --- a/reactos/ntoskrnl/ps/job.c +++ b/reactos/ntoskrnl/ps/job.c @@ -18,8 +18,6 @@ #pragma alloc_text(INIT, PsInitJobManagment) #endif - - /* GLOBALS *******************************************************************/ POBJECT_TYPE PsJobType = NULL; @@ -27,6 +25,22 @@ POBJECT_TYPE PsJobType = NULL; LIST_ENTRY PsJobListHead; static FAST_MUTEX PsJobListLock; +BOOLEAN PspUseJobSchedulingClasses; + +CHAR PspJobSchedulingClasses[PSP_JOB_SCHEDULING_CLASSES] = +{ + 1 * 6, + 2 * 6, + 3 * 6, + 4 * 6, + 5 * 6, + 6 * 6, + 7 * 6, + 8 * 6, + 9 * 6, + 10 * 6 +}; + static GENERIC_MAPPING PiJobMapping = { STANDARD_RIGHTS_READ | JOB_OBJECT_QUERY, diff --git a/reactos/ntoskrnl/ps/process.c b/reactos/ntoskrnl/ps/process.c index 735d8c28cae..3727aa82b41 100644 --- a/reactos/ntoskrnl/ps/process.c +++ b/reactos/ntoskrnl/ps/process.c @@ -27,6 +27,49 @@ KGUARDED_MUTEX PspActiveProcessMutex; LARGE_INTEGER ShortPsLockDelay; +ULONG PsPrioritySeparation; +CHAR PspForegroundQuantum[3]; + +/* Fixed quantum table */ +CHAR PspFixedQuantums[6] = +{ + /* Short quantums */ + 3 * 6, /* Level 1 */ + 3 * 6, /* Level 2 */ + 3 * 6, /* Level 3 */ + + /* Long quantums */ + 6 * 6, /* Level 1 */ + 6 * 6, /* Level 2 */ + 6 * 6 /* Level 3 */ +}; + +/* Variable quantum table */ +CHAR PspVariableQuantums[6] = +{ + /* Short quantums */ + 1 * 6, /* Level 1 */ + 2 * 6, /* Level 2 */ + 3 * 6, /* Level 3 */ + + /* Long quantums */ + 2 * 6, /* Level 1 */ + 4 * 6, /* Level 2 */ + 6 * 6 /* Level 3 */ +}; + +/* Priority table */ +KPRIORITY PspPriorityTable[PROCESS_PRIORITY_CLASS_ABOVE_NORMAL + 1] = +{ + 8, + 4, + 8, + 13, + 24, + 6, + 10 +}; + /* PRIVATE FUNCTIONS *********************************************************/ NTSTATUS @@ -142,6 +185,164 @@ PsGetNextProcess(IN PEPROCESS OldProcess) return FoundProcess; } +KPRIORITY +NTAPI +PspComputeQuantumAndPriority(IN PEPROCESS Process, + IN PSPROCESSPRIORITYMODE Mode, + OUT PCHAR Quantum) +{ + ULONG i; + UCHAR LocalQuantum, MemoryPriority; + PAGED_CODE(); + + /* Check if this is a foreground process */ + if (Mode == PsProcessPriorityForeground) + { + /* Set the memory priority and use priority separation */ + MemoryPriority = 2; + i = PsPrioritySeparation; + } + else + { + /* Set the background memory priority and no separation */ + MemoryPriority = 0; + i = 0; + } + + /* Make sure that the process mode isn't spinning */ + if (Mode != PsProcessPrioritySpinning) + { + /* Set the priority */ + MmSetMemoryPriorityProcess(Process, MemoryPriority); + } + + /* Make sure that the process isn't idle */ + if (Process->PriorityClass != PROCESS_PRIORITY_CLASS_IDLE) + { + /* Does the process have a job? */ + if ((Process->Job) && (PspUseJobSchedulingClasses)) + { + /* Use job quantum */ + LocalQuantum = PspJobSchedulingClasses[Process->Job-> + SchedulingClass]; + } + else + { + /* Use calculated quantum */ + LocalQuantum = PspForegroundQuantum[i]; + } + } + else + { + /* Process is idle, use default quantum */ + LocalQuantum = 6; + } + + /* Return quantum to caller */ + *Quantum = LocalQuantum; + + /* Return priority */ + return PspPriorityTable[Process->PriorityClass]; +} + +VOID +NTAPI +PsChangeQuantumTable(IN BOOLEAN Immediate, + IN ULONG PrioritySeparation) +{ + PEPROCESS Process = NULL; + ULONG i; + UCHAR Quantum; + PCHAR QuantumTable; + PAGED_CODE(); + + /* Write the current priority separation */ + PsPrioritySeparation = PspPrioritySeparationFromMask(PrioritySeparation); + + /* Normalize it if it was too high */ + if (PsPrioritySeparation == 3) PsPrioritySeparation = 2; + + /* Get the quantum table to use */ + if (PspQuantumTypeFromMask(PrioritySeparation) == PSP_VARIABLE_QUANTUMS) + { + /* Use a variable table */ + QuantumTable = PspVariableQuantums; + } + else + { + /* Use fixed table */ + QuantumTable = PspFixedQuantums; + } + + /* Now check if we should use long or short */ + if (PspQuantumLengthFromMask(PrioritySeparation) == PSP_LONG_QUANTUMS) + { + /* Use long quantums */ + QuantumTable += 3; + } + + /* Check if we're using long fixed quantums */ + if (QuantumTable == &PspFixedQuantums[3]) + { + /* Use Job scheduling classes */ + PspUseJobSchedulingClasses = TRUE; + } + else + { + /* Otherwise, we don't */ + PspUseJobSchedulingClasses = FALSE; + } + + /* Copy the selected table into the Foreground Quantum table */ + RtlCopyMemory(PspForegroundQuantum, + QuantumTable, + sizeof(PspForegroundQuantum)); + + /* Check if we should apply these changes real-time */ + if (Immediate) + { + /* We are...loop every process */ + Process == PsGetNextProcess(Process); + while (Process) + { + /* + * Use the priority separation, unless the process has + * low memory priority + */ + i = (Process->Vm.Flags.MemoryPriority == 1) ? + 0: PsPrioritySeparation; + + /* Make sure that the process isn't idle */ + if (Process->PriorityClass != PROCESS_PRIORITY_CLASS_IDLE) + { + /* Does the process have a job? */ + if ((Process->Job) && (PspUseJobSchedulingClasses)) + { + /* Use job quantum */ + Quantum = PspJobSchedulingClasses[Process->Job-> + SchedulingClass]; + } + else + { + /* Use calculated quantum */ + Quantum = PspForegroundQuantum[i]; + } + } + else + { + /* Process is idle, use default quantum */ + Quantum = 6; + } + + /* Now set the quantum */ + KeSetQuantumProcess(&Process->Pcb, Quantum); + + /* Get the next process */ + Process == PsGetNextProcess(Process); + } + } +} + NTSTATUS NTAPI PspCreateProcess(OUT PHANDLE ProcessHandle, @@ -861,15 +1062,21 @@ PsSetProcessWindowStation(PEPROCESS Process, } /* - * @unimplemented + * @implemented */ -NTSTATUS +VOID NTAPI PsSetProcessPriorityByClass(IN PEPROCESS Process, - IN ULONG Type) + IN PSPROCESSPRIORITYMODE Type) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + UCHAR Quantum; + ULONG Priority; + + /* Compute quantum and priority */ + Priority = PspComputeQuantumAndPriority(Process, Type, &Quantum); + + /* Set them */ + KeSetPriorityAndQuantumProcess(&Process->Pcb, Priority, Quantum); } /*