Reworked kernel timer lock

This commit is contained in:
ergo720 2019-01-08 16:44:56 +01:00
parent 4f86a5a26f
commit 5b8ae8393b
3 changed files with 63 additions and 32 deletions

View File

@ -449,6 +449,8 @@ XBSYSAPI EXPORTNUM(96) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeCancelTimer
assert(Timer); assert(Timer);
KiTimerLock();
/* Lock the Database and Raise IRQL */ /* Lock the Database and Raise IRQL */
KiLockDispatcherDatabase(&OldIrql); KiLockDispatcherDatabase(&OldIrql);
@ -461,6 +463,8 @@ XBSYSAPI EXPORTNUM(96) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeCancelTimer
/* Release Dispatcher Lock */ /* Release Dispatcher Lock */
KiUnlockDispatcherDatabase(OldIrql); KiUnlockDispatcherDatabase(OldIrql);
KiTimerUnlock();
/* Return the old state */ /* Return the old state */
RETURN(Inserted); RETURN(Inserted);
} }
@ -1686,6 +1690,8 @@ XBSYSAPI EXPORTNUM(150) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSetTimerEx
assert(Timer); assert(Timer);
assert(Timer->Header.Type == TimerNotificationObject || Timer->Header.Type == TimerSynchronizationObject); assert(Timer->Header.Type == TimerNotificationObject || Timer->Header.Type == TimerSynchronizationObject);
KiTimerLock();
KiLockDispatcherDatabase(&OldIrql); KiLockDispatcherDatabase(&OldIrql);
// Same as KeCancelTimer(Timer) : // Same as KeCancelTimer(Timer) :
@ -1718,6 +1724,8 @@ XBSYSAPI EXPORTNUM(150) xboxkrnl::BOOLEAN NTAPI xboxkrnl::KeSetTimerEx
/* Exit the dispatcher */ /* Exit the dispatcher */
KiUnlockDispatcherDatabase(OldIrql); KiUnlockDispatcherDatabase(OldIrql);
KiTimerUnlock();
RETURN(Inserted); RETURN(Inserted);
} }
@ -1946,6 +1954,7 @@ XBSYSAPI EXPORTNUM(158) xboxkrnl::NTSTATUS NTAPI xboxkrnl::KeWaitForMultipleObje
} }
// Setup a timer for the thread // Setup a timer for the thread
KiTimerLock();
PKTIMER Timer = &Thread->Timer; PKTIMER Timer = &Thread->Timer;
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock; PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
WaitBlock->NextWaitBlock = WaitTimer; WaitBlock->NextWaitBlock = WaitTimer;
@ -1954,10 +1963,12 @@ XBSYSAPI EXPORTNUM(158) xboxkrnl::NTSTATUS NTAPI xboxkrnl::KeWaitForMultipleObje
WaitTimer->NextWaitBlock = WaitBlock; WaitTimer->NextWaitBlock = WaitBlock;
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) { if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
WaitStatus = (NTSTATUS)STATUS_TIMEOUT; WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
KiTimerUnlock();
goto NoWait; goto NoWait;
} }
DueTime.QuadPart = Timer->DueTime.QuadPart; DueTime.QuadPart = Timer->DueTime.QuadPart;
KiTimerUnlock();
} }
else { else {
WaitBlock->NextWaitBlock = WaitBlock; WaitBlock->NextWaitBlock = WaitBlock;
@ -2128,6 +2139,7 @@ XBSYSAPI EXPORTNUM(159) xboxkrnl::NTSTATUS NTAPI xboxkrnl::KeWaitForSingleObject
} }
// Setup a timer for the thread // Setup a timer for the thread
KiTimerLock();
PKTIMER Timer = &Thread->Timer; PKTIMER Timer = &Thread->Timer;
PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock; PKWAIT_BLOCK WaitTimer = &Thread->TimerWaitBlock;
WaitBlock->NextWaitBlock = WaitTimer; WaitBlock->NextWaitBlock = WaitTimer;
@ -2136,10 +2148,12 @@ XBSYSAPI EXPORTNUM(159) xboxkrnl::NTSTATUS NTAPI xboxkrnl::KeWaitForSingleObject
WaitTimer->NextWaitBlock = WaitBlock; WaitTimer->NextWaitBlock = WaitBlock;
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) { if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
WaitStatus = (NTSTATUS)STATUS_TIMEOUT; WaitStatus = (NTSTATUS)STATUS_TIMEOUT;
KiTimerUnlock();
goto NoWait; goto NoWait;
} }
DueTime.QuadPart = Timer->DueTime.QuadPart; DueTime.QuadPart = Timer->DueTime.QuadPart;
KiTimerUnlock();
} }
else { else {
WaitBlock->NextWaitBlock = WaitBlock; WaitBlock->NextWaitBlock = WaitBlock;

View File

@ -36,7 +36,7 @@
// * // *
// ****************************************************************** // ******************************************************************
// Acknowledgment: ReactOS (GPLv2) // Acknowledgment (timer functions): ReactOS (GPLv2)
// https://github.com/reactos/reactos // https://github.com/reactos/reactos
// Changed from ReactOS: slight changes to the Hand parameter usage // Changed from ReactOS: slight changes to the Hand parameter usage
@ -58,6 +58,8 @@ namespace xboxkrnl
// ReactOS uses a size of 512, but disassembling the kernel reveals it to be 32 instead // ReactOS uses a size of 512, but disassembling the kernel reveals it to be 32 instead
#define TIMER_TABLE_SIZE 32 #define TIMER_TABLE_SIZE 32
#define ASSERT_TIMER_LOCKED assert(xboxkrnl::KiTimerMtx.Acquired == true)
xboxkrnl::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE]; xboxkrnl::KTIMER_TABLE_ENTRY KiTimerTableListHead[TIMER_TABLE_SIZE];
xboxkrnl::KDPC KiTimerExpireDpc; xboxkrnl::KDPC KiTimerExpireDpc;
@ -68,6 +70,7 @@ VOID xboxkrnl::KiInitSystem()
InitializeListHead(&KiWaitInListHead); InitializeListHead(&KiWaitInListHead);
KiTimerMtx.Acquired = false;
KeInitializeDpc(&KiTimerExpireDpc, KiTimerExpiration, NULL); KeInitializeDpc(&KiTimerExpireDpc, KiTimerExpiration, NULL);
for (i = 0; i < TIMER_TABLE_SIZE; i++) { for (i = 0; i < TIMER_TABLE_SIZE; i++) {
InitializeListHead(&KiTimerTableListHead[i].Entry); InitializeListHead(&KiTimerTableListHead[i].Entry);
@ -76,6 +79,18 @@ VOID xboxkrnl::KiInitSystem()
} }
} }
VOID xboxkrnl::KiTimerLock()
{
KiTimerMtx.Mtx.lock();
KiTimerMtx.Acquired = true;
}
VOID xboxkrnl::KiTimerUnlock()
{
KiTimerMtx.Mtx.unlock();
KiTimerMtx.Acquired = false;
}
VOID xboxkrnl::KiClockIsr(unsigned int ScalingFactor) VOID xboxkrnl::KiClockIsr(unsigned int ScalingFactor)
{ {
KIRQL OldIrql; KIRQL OldIrql;
@ -107,14 +122,16 @@ VOID xboxkrnl::KiClockIsr(unsigned int ScalingFactor)
// Because this function must be fast to continuously update the kernel clocks, if somebody else is currently // Because this function must be fast to continuously update the kernel clocks, if somebody else is currently
// holding the lock, we won't wait and instead skip the check of the timers for this cycle // holding the lock, we won't wait and instead skip the check of the timers for this cycle
if (TimerMtx.try_lock()) { if (KiTimerMtx.Mtx.try_lock()) {
KiTimerMtx.Acquired = true;
// Check if a timer has expired // Check if a timer has expired
Hand = KeTickCount & (TIMER_TABLE_SIZE - 1); Hand = KeTickCount & (TIMER_TABLE_SIZE - 1);
if (KiTimerTableListHead[Hand].Entry.Flink != &KiTimerTableListHead[Hand].Entry && if (KiTimerTableListHead[Hand].Entry.Flink != &KiTimerTableListHead[Hand].Entry &&
InterruptTime.QuadPart >= KiTimerTableListHead[Hand].Time.QuadPart) { InterruptTime.QuadPart >= KiTimerTableListHead[Hand].Time.QuadPart) {
KeInsertQueueDpc(&KiTimerExpireDpc, (PVOID)&KeTickCount, 0); KeInsertQueueDpc(&KiTimerExpireDpc, (PVOID)&KeTickCount, 0);
} }
TimerMtx.unlock(); KiTimerMtx.Mtx.unlock();
KiTimerMtx.Acquired = false;
} }
KfLowerIrql(OldIrql); KfLowerIrql(OldIrql);
@ -125,7 +142,7 @@ VOID xboxkrnl::KxInsertTimer(
IN xboxkrnl::ULONG Hand IN xboxkrnl::ULONG Hand
) )
{ {
TimerMtx.lock(); ASSERT_TIMER_LOCKED;
/* Try to insert the timer */ /* Try to insert the timer */
if (KiInsertTimerTable(Timer, Hand)) if (KiInsertTimerTable(Timer, Hand))
@ -133,7 +150,6 @@ VOID xboxkrnl::KxInsertTimer(
/* Complete it */ /* Complete it */
KiCompleteTimer(Timer, Hand); KiCompleteTimer(Timer, Hand);
} }
TimerMtx.unlock();
} }
VOID FASTCALL xboxkrnl::KiCompleteTimer( VOID FASTCALL xboxkrnl::KiCompleteTimer(
@ -144,7 +160,7 @@ VOID FASTCALL xboxkrnl::KiCompleteTimer(
LIST_ENTRY ListHead; LIST_ENTRY ListHead;
BOOLEAN RequestInterrupt = FALSE; BOOLEAN RequestInterrupt = FALSE;
TimerMtx.lock(); ASSERT_TIMER_LOCKED;
/* Remove it from the timer list */ /* Remove it from the timer list */
KiRemoveEntryTimer(Timer, Hand); KiRemoveEntryTimer(Timer, Hand);
@ -164,7 +180,6 @@ VOID FASTCALL xboxkrnl::KiCompleteTimer(
if (RequestInterrupt) { if (RequestInterrupt) {
HalRequestSoftwareInterrupt(DISPATCH_LEVEL); HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
} }
TimerMtx.unlock();
} }
VOID xboxkrnl::KiRemoveEntryTimer( VOID xboxkrnl::KiRemoveEntryTimer(
@ -175,7 +190,7 @@ VOID xboxkrnl::KiRemoveEntryTimer(
ULONG Hand; ULONG Hand;
PKTIMER_TABLE_ENTRY TableEntry; PKTIMER_TABLE_ENTRY TableEntry;
TimerMtx.lock(); ASSERT_TIMER_LOCKED;
/* Remove the timer from the timer list and check if it's empty */ /* Remove the timer from the timer list and check if it's empty */
if (RemoveEntryList(&Timer->TimerListEntry)) if (RemoveEntryList(&Timer->TimerListEntry))
@ -192,7 +207,6 @@ VOID xboxkrnl::KiRemoveEntryTimer(
/* Clear the list entries so we can tell the timer is gone */ /* Clear the list entries so we can tell the timer is gone */
Timer->TimerListEntry.Flink = NULL; Timer->TimerListEntry.Flink = NULL;
Timer->TimerListEntry.Blink = NULL; Timer->TimerListEntry.Blink = NULL;
TimerMtx.unlock();
} }
VOID xboxkrnl::KxRemoveTreeTimer( VOID xboxkrnl::KxRemoveTreeTimer(
@ -202,7 +216,7 @@ VOID xboxkrnl::KxRemoveTreeTimer(
ULONG Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart); ULONG Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart);
PKTIMER_TABLE_ENTRY TimerEntry; PKTIMER_TABLE_ENTRY TimerEntry;
TimerMtx.lock(); ASSERT_TIMER_LOCKED;
/* Set the timer as non-inserted */ /* Set the timer as non-inserted */
Timer->Header.Inserted = FALSE; Timer->Header.Inserted = FALSE;
@ -218,7 +232,6 @@ VOID xboxkrnl::KxRemoveTreeTimer(
TimerEntry->Time.u.HighPart = 0xFFFFFFFF; TimerEntry->Time.u.HighPart = 0xFFFFFFFF;
} }
} }
TimerMtx.unlock();
} }
xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTimerTable( xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTimerTable(
@ -234,7 +247,7 @@ xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTimerTable(
DBG_PRINTF("%s: inserting Timer %p, Hand: %lu\n", __func__, Timer, Hand); DBG_PRINTF("%s: inserting Timer %p, Hand: %lu\n", __func__, Timer, Hand);
TimerMtx.lock(); ASSERT_TIMER_LOCKED;
/* Check if the period is zero */ /* Check if the period is zero */
if (!Timer->Period) { if (!Timer->Period) {
@ -272,7 +285,6 @@ xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTimerTable(
Expired = TRUE; Expired = TRUE;
} }
} }
TimerMtx.unlock();
/* Return expired state */ /* Return expired state */
return Expired; return Expired;
@ -286,7 +298,7 @@ xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTreeTimer(
BOOLEAN Inserted = FALSE; BOOLEAN Inserted = FALSE;
ULONG Hand = 0; ULONG Hand = 0;
TimerMtx.lock(); ASSERT_TIMER_LOCKED;
/* Setup the timer's due time */ /* Setup the timer's due time */
if (KiComputeDueTime(Timer, Interval, &Hand)) if (KiComputeDueTime(Timer, Interval, &Hand))
@ -304,7 +316,6 @@ xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiInsertTreeTimer(
Inserted = TRUE; Inserted = TRUE;
} }
} }
TimerMtx.unlock();
return Inserted; return Inserted;
} }
@ -323,7 +334,7 @@ xboxkrnl::BOOLEAN xboxkrnl::KiComputeDueTime(
{ {
LARGE_INTEGER InterruptTime, SystemTime, DifferenceTime; LARGE_INTEGER InterruptTime, SystemTime, DifferenceTime;
TimerMtx.lock(); ASSERT_TIMER_LOCKED;
/* Convert to relative time if needed */ /* Convert to relative time if needed */
Timer->Header.Absolute = FALSE; Timer->Header.Absolute = FALSE;
@ -344,8 +355,6 @@ xboxkrnl::BOOLEAN xboxkrnl::KiComputeDueTime(
Timer->Header.SignalState = TRUE; Timer->Header.SignalState = TRUE;
Timer->DueTime.QuadPart = 0; Timer->DueTime.QuadPart = 0;
*Hand = 0; *Hand = 0;
TimerMtx.unlock();
return FALSE; return FALSE;
} }
@ -362,7 +371,6 @@ xboxkrnl::BOOLEAN xboxkrnl::KiComputeDueTime(
/* Get the handle */ /* Get the handle */
*Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart); *Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart);
Timer->Header.Inserted = TRUE; Timer->Header.Inserted = TRUE;
TimerMtx.unlock();
return TRUE; return TRUE;
} }
@ -376,7 +384,7 @@ xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiSignalTimer(
ULONG Period = Timer->Period; ULONG Period = Timer->Period;
LARGE_INTEGER Interval, SystemTime; LARGE_INTEGER Interval, SystemTime;
TimerMtx.lock(); ASSERT_TIMER_LOCKED;
/* Set default values */ /* Set default values */
Timer->Header.Inserted = FALSE; Timer->Header.Inserted = FALSE;
@ -417,8 +425,6 @@ xboxkrnl::BOOLEAN FASTCALL xboxkrnl::KiSignalTimer(
RequestInterrupt = TRUE; RequestInterrupt = TRUE;
} }
TimerMtx.unlock();
/* Return whether we need to request a DPC interrupt or not */ /* Return whether we need to request a DPC interrupt or not */
return RequestInterrupt; return RequestInterrupt;
} }

View File

@ -43,14 +43,25 @@ namespace xboxkrnl
ULARGE_INTEGER Time; ULARGE_INTEGER Time;
} KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY; } KTIMER_TABLE_ENTRY, *PKTIMER_TABLE_ENTRY;
const ULONG CLOCK_TIME_INCREMENT = 0x2710;
LIST_ENTRY KiWaitInListHead;
// Actually, this lock isn't required, but because raising the irql to dpc level doesn't really prevent thread switching at the // Actually, this lock isn't required, but because raising the irql to dpc level doesn't really prevent thread switching at the
// moment, we need it for now to prevent concurrent access to the timer table // moment, we need it for now to prevent concurrent access to the timer table
std::recursive_mutex TimerMtx; typedef struct _KI_TIMER_LOCK
{
std::recursive_mutex Mtx;
bool Acquired;
} KI_TIMER_LOCK;
const ULONG CLOCK_TIME_INCREMENT = 0x2710;
LIST_ENTRY KiWaitInListHead;
KI_TIMER_LOCK KiTimerMtx;
VOID KiInitSystem(); VOID KiInitSystem();
VOID KiTimerLock();
VOID KiTimerUnlock();
VOID KiClockIsr( VOID KiClockIsr(
IN unsigned int ScalingFactor IN unsigned int ScalingFactor
); );