VOID FASTCALL KiCompleteTimer(IN PKTIMER Timer, IN PKSPIN_LOCK_QUEUE LockQueue) { LIST_ENTRY ListHead; BOOLEAN RequestInterrupt = FALSE; DPRINT("KiCompleteTimer(): Timer %p, LockQueue: %p\n", Timer, LockQueue); /* Remove it from the timer list */ KiRemoveEntryTimer(Timer); /* Link the timer list to our stack */ ListHead.Flink = &Timer->TimerListEntry; ListHead.Blink = &Timer->TimerListEntry; Timer->TimerListEntry.Flink = &ListHead; Timer->TimerListEntry.Blink = &ListHead; /* Release the timer lock */ KiReleaseTimerLock(LockQueue); /* Acquire dispatcher lock */ KiAcquireDispatcherLockAtDpcLevel(); /* Signal the timer if it's still on our list */ if (!IsListEmpty(&ListHead)) RequestInterrupt = KiSignalTimer(Timer); /* Release the dispatcher lock */ KiReleaseDispatcherLockFromDpcLevel(); /* Request a DPC if needed */ if (RequestInterrupt) HalRequestSoftwareInterrupt(DISPATCH_LEVEL); }
BOOLEAN FASTCALL KiInsertTreeTimer(IN PKTIMER Timer, IN LARGE_INTEGER Interval) { BOOLEAN Inserted = FALSE; ULONG Hand = 0; PKSPIN_LOCK_QUEUE LockQueue; DPRINT("KiInsertTreeTimer(): Timer %p, Interval: %I64d\n", Timer, Interval.QuadPart); /* Setup the timer's due time */ if (KiComputeDueTime(Timer, Interval, &Hand)) { /* Acquire the lock */ LockQueue = KiAcquireTimerLock(Hand); /* Insert the timer */ if (KiInsertTimerTable(Timer, Hand)) { /* It was already there, remove it */ KiRemoveEntryTimer(Timer); Timer->Header.Inserted = FALSE; } else { /* Otherwise, we're now inserted */ Inserted = TRUE; } /* Release the lock */ KiReleaseTimerLock(LockQueue); } /* Release the lock and return insert status */ return Inserted; }
VOID NTAPI KiTimerExpiration(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { ULARGE_INTEGER SystemTime, InterruptTime; LARGE_INTEGER Interval; LONG Limit, Index, i; ULONG Timers, ActiveTimers, DpcCalls; PLIST_ENTRY ListHead, NextEntry; KIRQL OldIrql; PKTIMER Timer; PKDPC TimerDpc; ULONG Period; DPC_QUEUE_ENTRY DpcEntry[MAX_TIMER_DPCS]; PKSPIN_LOCK_QUEUE LockQueue; #ifdef CONFIG_SMP PKPRCB Prcb = KeGetCurrentPrcb(); #endif /* Disable interrupts */ _disable(); /* Query system and interrupt time */ KeQuerySystemTime((PLARGE_INTEGER)&SystemTime); InterruptTime.QuadPart = KeQueryInterruptTime(); Limit = KeTickCount.LowPart; /* Bring interrupts back */ _enable(); /* Get the index of the timer and normalize it */ Index = PtrToLong(SystemArgument1); if ((Limit - Index) >= TIMER_TABLE_SIZE) { /* Normalize it */ Limit = Index + TIMER_TABLE_SIZE - 1; } /* Setup index and actual limit */ Index--; Limit &= (TIMER_TABLE_SIZE - 1); /* Setup accounting data */ DpcCalls = 0; Timers = 24; ActiveTimers = 4; /* Lock the Database and Raise IRQL */ OldIrql = KiAcquireDispatcherLock(); /* Start expiration loop */ do { /* Get the current index */ Index = (Index + 1) & (TIMER_TABLE_SIZE - 1); /* Get list pointers and loop the list */ ListHead = &KiTimerTableListHead[Index].Entry; while (ListHead != ListHead->Flink) { /* Lock the timer and go to the next entry */ LockQueue = KiAcquireTimerLock(Index); NextEntry = ListHead->Flink; /* Get the current timer and check its due time */ Timers--; Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); if ((NextEntry != ListHead) && (Timer->DueTime.QuadPart <= InterruptTime.QuadPart)) { /* It's expired, remove it */ ActiveTimers--; KiRemoveEntryTimer(Timer); /* Make it non-inserted, unlock it, and signal it */ Timer->Header.Inserted = FALSE; KiReleaseTimerLock(LockQueue); Timer->Header.SignalState = 1; /* Get the DPC and period */ TimerDpc = Timer->Dpc; Period = Timer->Period; /* Check if there's any waiters */ if (!IsListEmpty(&Timer->Header.WaitListHead)) { /* Check the type of event */ if (Timer->Header.Type == TimerNotificationObject) { /* Unwait the thread */ KxUnwaitThread(&Timer->Header, IO_NO_INCREMENT); } else { /* Otherwise unwait the thread and signal the timer */ KxUnwaitThreadForEvent((PKEVENT)Timer, IO_NO_INCREMENT); } } /* Check if we have a period */ if (Period) { /* Calculate the interval and insert the timer */ Interval.QuadPart = Int32x32To64(Period, -10000); while (!KiInsertTreeTimer(Timer, Interval)); } /* Check if we have a DPC */ if (TimerDpc) { #ifdef CONFIG_SMP /* * If the DPC is targeted to another processor, * then insert it into that processor's DPC queue * instead of delivering it now. * If the DPC is a threaded DPC, and the current CPU * has threaded DPCs enabled (KiExecuteDpc is actively parsing DPCs), * then also insert it into the DPC queue for threaded delivery, * instead of doing it here. */ if (((TimerDpc->Number >= MAXIMUM_PROCESSORS) && ((TimerDpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) || ((TimerDpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))) { /* Queue it */ KeInsertQueueDpc(TimerDpc, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } else #endif { /* Setup the DPC Entry */ DpcEntry[DpcCalls].Dpc = TimerDpc; DpcEntry[DpcCalls].Routine = TimerDpc->DeferredRoutine; DpcEntry[DpcCalls].Context = TimerDpc->DeferredContext; DpcCalls++; ASSERT(DpcCalls < MAX_TIMER_DPCS); } } /* Check if we're done processing */ if (!(ActiveTimers) || !(Timers)) { /* Release the dispatcher while doing DPCs */ KiReleaseDispatcherLock(DISPATCH_LEVEL); /* Start looping all DPC Entries */ for (i = 0; DpcCalls; DpcCalls--, i++) { /* Call the DPC */ DpcEntry[i].Routine(DpcEntry[i].Dpc, DpcEntry[i].Context, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } /* Reset accounting */ Timers = 24; ActiveTimers = 4; /* Lock the dispatcher database */ KiAcquireDispatcherLock(); } } else { /* Check if the timer list is empty */ if (NextEntry != ListHead) { /* Sanity check */ ASSERT(KiTimerTableListHead[Index].Time.QuadPart <= Timer->DueTime.QuadPart); /* Update the time */ _disable(); KiTimerTableListHead[Index].Time.QuadPart = Timer->DueTime.QuadPart; _enable(); } /* Release the lock */ KiReleaseTimerLock(LockQueue); /* Check if we've scanned all the timers we could */ if (!Timers) { /* Release the dispatcher while doing DPCs */ KiReleaseDispatcherLock(DISPATCH_LEVEL); /* Start looping all DPC Entries */ for (i = 0; DpcCalls; DpcCalls--, i++) { /* Call the DPC */ DpcEntry[i].Routine(DpcEntry[i].Dpc, DpcEntry[i].Context, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } /* Reset accounting */ Timers = 24; ActiveTimers = 4; /* Lock the dispatcher database */ KiAcquireDispatcherLock(); } /* Done looping */ break; } } } while (Index != Limit); /* Verify the timer table, on debug builds */ if (KeNumberProcessors == 1) KiCheckTimerTable(InterruptTime); /* Check if we still have DPC entries */ if (DpcCalls) { /* Release the dispatcher while doing DPCs */ KiReleaseDispatcherLock(DISPATCH_LEVEL); /* Start looping all DPC Entries */ for (i = 0; DpcCalls; DpcCalls--, i++) { /* Call the DPC */ DpcEntry[i].Routine(DpcEntry[i].Dpc, DpcEntry[i].Context, UlongToPtr(SystemTime.LowPart), UlongToPtr(SystemTime.HighPart)); } /* Lower IRQL if we need to */ if (OldIrql != DISPATCH_LEVEL) KeLowerIrql(OldIrql); } else { /* Unlock the dispatcher */ KiReleaseDispatcherLock(OldIrql); } }
VOID NTAPI KeSetSystemTime(IN PLARGE_INTEGER NewTime, OUT PLARGE_INTEGER OldTime, IN BOOLEAN FixInterruptTime, IN PLARGE_INTEGER HalTime OPTIONAL) { TIME_FIELDS TimeFields; KIRQL OldIrql, OldIrql2; LARGE_INTEGER DeltaTime; PLIST_ENTRY ListHead, NextEntry; PKTIMER Timer; PKSPIN_LOCK_QUEUE LockQueue; LIST_ENTRY TempList, TempList2; ULONG Hand, i; /* Sanity checks */ ASSERT((NewTime->HighPart & 0xF0000000) == 0); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); /* Check if this is for the HAL */ if (HalTime) RtlTimeToTimeFields(HalTime, &TimeFields); /* Set affinity to this CPU, lock the dispatcher, and raise IRQL */ KeSetSystemAffinityThread(1); OldIrql = KiAcquireDispatcherLock(); KeRaiseIrql(HIGH_LEVEL, &OldIrql2); /* Query the system time now */ KeQuerySystemTime(OldTime); /* Set the new system time (ordering of these operations is critical) */ SharedUserData->SystemTime.High2Time = NewTime->HighPart; SharedUserData->SystemTime.LowPart = NewTime->LowPart; SharedUserData->SystemTime.High1Time = NewTime->HighPart; /* Check if this was for the HAL and set the RTC time */ if (HalTime) ExCmosClockIsSane = HalSetRealTimeClock(&TimeFields); /* Calculate the difference between the new and the old time */ DeltaTime.QuadPart = NewTime->QuadPart - OldTime->QuadPart; /* Update system boot time */ KeBootTime.QuadPart += DeltaTime.QuadPart; KeBootTimeBias = KeBootTimeBias + DeltaTime.QuadPart; /* Lower IRQL back */ KeLowerIrql(OldIrql2); /* Check if we need to adjust interrupt time */ if (FixInterruptTime) ASSERT(FALSE); /* Setup a temporary list of absolute timers */ InitializeListHead(&TempList); /* Loop current timers */ for (i = 0; i < TIMER_TABLE_SIZE; i++) { /* Loop the entries in this table and lock the timers */ ListHead = &KiTimerTableListHead[i].Entry; LockQueue = KiAcquireTimerLock(i); NextEntry = ListHead->Flink; while (NextEntry != ListHead) { /* Get the timer */ Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry); NextEntry = NextEntry->Flink; /* Is it absolute? */ if (Timer->Header.Absolute) { /* Remove it from the timer list */ KiRemoveEntryTimer(Timer); /* Insert it into our temporary list */ InsertTailList(&TempList, &Timer->TimerListEntry); } } /* Release the lock */ KiReleaseTimerLock(LockQueue); } /* Setup a temporary list of expired timers */ InitializeListHead(&TempList2); /* Loop absolute timers */ while (TempList.Flink != &TempList) { /* Get the timer */ Timer = CONTAINING_RECORD(TempList.Flink, KTIMER, TimerListEntry); RemoveEntryList(&Timer->TimerListEntry); /* Update the due time and handle */ Timer->DueTime.QuadPart -= DeltaTime.QuadPart; Hand = KiComputeTimerTableIndex(Timer->DueTime.QuadPart); Timer->Header.Hand = (UCHAR)Hand; /* Lock the timer and re-insert it */ LockQueue = KiAcquireTimerLock(Hand); if (KiInsertTimerTable(Timer, Hand)) { /* Remove it from the timer list */ KiRemoveEntryTimer(Timer); /* Insert it into our temporary list */ InsertTailList(&TempList2, &Timer->TimerListEntry); } /* Release the lock */ KiReleaseTimerLock(LockQueue); } /* Process expired timers. This releases the dispatcher lock. */ KiTimerListExpire(&TempList2, OldIrql); /* Revert affinity */ KeRevertToUserAffinityThread(); }