507 lines
13 KiB
C
507 lines
13 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
NtTimer.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the nt version of the timer and worker thread management routines.
|
|||
|
These services are provided to all mini redirector writers. The timer service comes in two
|
|||
|
flavours - a periodic trigger and a one shot notification.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Joe Linn [JoeLinn] 2-mar-95
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
Balan Sethu Raman [SethuR] 7-Mar-95
|
|||
|
Included one shot, periodic notification for work queue items.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, RxInitializeRxTimer)
|
|||
|
#pragma alloc_text(PAGE, RxTearDownRxTimer)
|
|||
|
#pragma alloc_text(PAGE, RxPostRecurrentTimerRequest)
|
|||
|
#pragma alloc_text(PAGE, RxRecurrentTimerWorkItemDispatcher)
|
|||
|
#endif
|
|||
|
|
|||
|
typedef struct _RX_RECURRENT_WORK_ITEM_ {
|
|||
|
RX_WORK_ITEM WorkItem;
|
|||
|
LIST_ENTRY RecurrentWorkItemsList;
|
|||
|
LARGE_INTEGER TimeInterval;
|
|||
|
PRX_WORKERTHREAD_ROUTINE Routine;
|
|||
|
PVOID pContext;
|
|||
|
} RX_RECURRENT_WORK_ITEM, *PRX_RECURRENT_WORK_ITEM;
|
|||
|
|
|||
|
//
|
|||
|
// Forward declarations of routines
|
|||
|
//
|
|||
|
|
|||
|
extern VOID
|
|||
|
RxTimerDispatch(
|
|||
|
IN PKDPC Dpc,
|
|||
|
IN PVOID DeferredContext,
|
|||
|
IN PVOID SystemArgument1,
|
|||
|
IN PVOID SystemArgument2
|
|||
|
);
|
|||
|
|
|||
|
extern VOID
|
|||
|
RxRecurrentTimerWorkItemDispatcher (
|
|||
|
IN PVOID Context
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
// The Bug check file id for this module
|
|||
|
#define BugCheckFileId (RDBSS_BUG_CHECK_NTTIMER)
|
|||
|
|
|||
|
|
|||
|
// The local trace mask for this part of the module
|
|||
|
#define Dbg (DEBUG_TRACE_NTTIMER)
|
|||
|
|
|||
|
LARGE_INTEGER s_RxTimerInterval;
|
|||
|
KSPIN_LOCK s_RxTimerLock;
|
|||
|
KDPC s_RxTimerDpc;
|
|||
|
LIST_ENTRY s_RxTimerQueueHead; // queue of the list of timer calls
|
|||
|
LIST_ENTRY s_RxRecurrentWorkItemsList;
|
|||
|
KTIMER s_RxTimer;
|
|||
|
ULONG s_RxTimerTickCount;
|
|||
|
|
|||
|
#define NoOf100nsTicksIn1ms (10 * 1000)
|
|||
|
#define NoOf100nsTicksIn55ms (10 * 1000 * 55)
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RxInitializeRxTimer()
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
The routine initializes everything having to do with the timer stuff.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
s_RxTimerInterval.LowPart = (ULONG)(-((LONG)NoOf100nsTicksIn55ms));
|
|||
|
s_RxTimerInterval.HighPart = -1;
|
|||
|
|
|||
|
KeInitializeSpinLock( &s_RxTimerLock );
|
|||
|
|
|||
|
InitializeListHead( &s_RxTimerQueueHead );
|
|||
|
InitializeListHead( &s_RxRecurrentWorkItemsList );
|
|||
|
|
|||
|
KeInitializeDpc( &s_RxTimerDpc, RxTimerDispatch, NULL );
|
|||
|
KeInitializeTimer( &s_RxTimer );
|
|||
|
|
|||
|
s_RxTimerTickCount = 0;
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
RxTearDownRxTimer(
|
|||
|
void)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used by drivers to initialize a timer entry for a device
|
|||
|
object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
TimerEntry - Pointer to a timer entry to be used.
|
|||
|
|
|||
|
TimerRoutine - Driver routine to be executed when timer expires.
|
|||
|
|
|||
|
Context - Context parameter that is passed to the driver routine.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value indicates whether or not the timer was initialized.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PRX_RECURRENT_WORK_ITEM pWorkItem;
|
|||
|
PLIST_ENTRY pListEntry;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
KeCancelTimer( &s_RxTimer );
|
|||
|
|
|||
|
// Walk down the list freeing up the recurrent requests since the memory was
|
|||
|
// allocated by us.
|
|||
|
while (!IsListEmpty(&s_RxRecurrentWorkItemsList)) {
|
|||
|
pListEntry = RemoveHeadList(&s_RxRecurrentWorkItemsList);
|
|||
|
pWorkItem = (PRX_RECURRENT_WORK_ITEM)
|
|||
|
CONTAINING_RECORD(
|
|||
|
pListEntry,
|
|||
|
RX_RECURRENT_WORK_ITEM,
|
|||
|
RecurrentWorkItemsList);
|
|||
|
RxFreePool(pWorkItem);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
RxTimerDispatch(
|
|||
|
IN PKDPC Dpc,
|
|||
|
IN PVOID DeferredContext,
|
|||
|
IN PVOID SystemArgument1,
|
|||
|
IN PVOID SystemArgument2
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine scans the timer database and posts a work item for all those requests
|
|||
|
whose temporal constraints have been satisfied.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Dpc - Supplies a pointer to a control object of type DPC.
|
|||
|
|
|||
|
DeferredContext - Optional deferred context; not used.
|
|||
|
|
|||
|
SystemArgument1 - Optional argument 1; not used.
|
|||
|
|
|||
|
SystemArgument2 - Optional argument 2; not used.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLIST_ENTRY pListEntry;
|
|||
|
LIST_ENTRY ExpiredList;
|
|||
|
|
|||
|
//KIRQL Irql;
|
|||
|
BOOLEAN ContinueTimer = FALSE;
|
|||
|
|
|||
|
PRX_WORK_QUEUE_ITEM pWorkQueueItem;
|
|||
|
PRX_WORK_ITEM pWorkItem;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( Dpc );
|
|||
|
UNREFERENCED_PARAMETER( DeferredContext );
|
|||
|
UNREFERENCED_PARAMETER( SystemArgument1 );
|
|||
|
UNREFERENCED_PARAMETER( SystemArgument2 );
|
|||
|
|
|||
|
InitializeListHead(&ExpiredList);
|
|||
|
|
|||
|
KeAcquireSpinLockAtDpcLevel( &s_RxTimerLock );
|
|||
|
|
|||
|
s_RxTimerTickCount++;
|
|||
|
|
|||
|
pListEntry = s_RxTimerQueueHead.Flink;
|
|||
|
|
|||
|
while (pListEntry != &s_RxTimerQueueHead) {
|
|||
|
pWorkQueueItem = CONTAINING_RECORD(
|
|||
|
pListEntry,
|
|||
|
RX_WORK_QUEUE_ITEM,
|
|||
|
List );
|
|||
|
pWorkItem = CONTAINING_RECORD(
|
|||
|
pWorkQueueItem,
|
|||
|
RX_WORK_ITEM,
|
|||
|
WorkQueueItem);
|
|||
|
|
|||
|
if (pWorkItem->LastTick == s_RxTimerTickCount) {
|
|||
|
PLIST_ENTRY pExpiredEntry = pListEntry;
|
|||
|
pListEntry = pListEntry->Flink;
|
|||
|
|
|||
|
RemoveEntryList(pExpiredEntry);
|
|||
|
InsertTailList(&ExpiredList,pExpiredEntry);
|
|||
|
} else {
|
|||
|
pListEntry = pListEntry->Flink;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ContinueTimer = !(IsListEmpty(&s_RxTimerQueueHead));
|
|||
|
KeReleaseSpinLockFromDpcLevel( &s_RxTimerLock );
|
|||
|
|
|||
|
// Resubmit the timer queue dispatch routine so that it will be reinvoked.
|
|||
|
if (ContinueTimer)
|
|||
|
KeSetTimer( &s_RxTimer, s_RxTimerInterval, &s_RxTimerDpc );
|
|||
|
|
|||
|
// Queue all the expired entries on the worker threads.
|
|||
|
while (!IsListEmpty(&ExpiredList)) {
|
|||
|
pListEntry = RemoveHeadList(&ExpiredList);
|
|||
|
pListEntry->Flink = pListEntry->Blink = NULL;
|
|||
|
|
|||
|
pWorkQueueItem = CONTAINING_RECORD(
|
|||
|
pListEntry,
|
|||
|
RX_WORK_QUEUE_ITEM,
|
|||
|
List );
|
|||
|
|
|||
|
// Post the work item to a worker thread
|
|||
|
RxPostToWorkerThread(
|
|||
|
pWorkQueueItem->pDeviceObject,
|
|||
|
CriticalWorkQueue,
|
|||
|
pWorkQueueItem,
|
|||
|
pWorkQueueItem->WorkerRoutine,
|
|||
|
pWorkQueueItem->Parameter);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RxPostOneShotTimerRequest(
|
|||
|
IN PRDBSS_DEVICE_OBJECT pDeviceObject,
|
|||
|
IN PRX_WORK_ITEM pWorkItem,
|
|||
|
IN PRX_WORKERTHREAD_ROUTINE Routine,
|
|||
|
IN PVOID pContext,
|
|||
|
IN LARGE_INTEGER TimeInterval)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used by drivers to initialize a timer entry for a device
|
|||
|
object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDeviceObject - the device object
|
|||
|
|
|||
|
pWorkItem - the work item
|
|||
|
|
|||
|
Routine - the routine to be invoked on timeout
|
|||
|
|
|||
|
pContext - the Context parameter that is passed to the driver routine.
|
|||
|
|
|||
|
TimeInterval - the time interval in 100 ns ticks.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value indicates whether or not the timer was initialized.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN StartTimer;
|
|||
|
//NTSTATUS Status;
|
|||
|
ULONG NumberOf55msIntervals;
|
|||
|
KIRQL Irql;
|
|||
|
LARGE_INTEGER StrobeInterval;
|
|||
|
|
|||
|
ASSERT(pWorkItem != NULL);
|
|||
|
|
|||
|
// Initialize the work queue item.
|
|||
|
ExInitializeWorkItem(
|
|||
|
(PWORK_QUEUE_ITEM)&pWorkItem->WorkQueueItem,
|
|||
|
Routine,
|
|||
|
pContext );
|
|||
|
|
|||
|
pWorkItem->WorkQueueItem.pDeviceObject = pDeviceObject;
|
|||
|
|
|||
|
// Compute the time interval in number of ticks.
|
|||
|
StrobeInterval.QuadPart= NoOf100nsTicksIn55ms;
|
|||
|
NumberOf55msIntervals = (ULONG)(TimeInterval.QuadPart / StrobeInterval.QuadPart);
|
|||
|
NumberOf55msIntervals += 1; // Take the ceiling to be conservative
|
|||
|
RxDbgTraceLV( 0, Dbg, 1500, ("Timer will expire after %ld 55ms intervals\n",NumberOf55msIntervals));
|
|||
|
|
|||
|
// Insert the entry in the timer queue.
|
|||
|
KeAcquireSpinLock( &s_RxTimerLock, &Irql );
|
|||
|
|
|||
|
// Update the tick relative to the current tick.
|
|||
|
pWorkItem->LastTick = s_RxTimerTickCount + NumberOf55msIntervals;
|
|||
|
|
|||
|
StartTimer = IsListEmpty(&s_RxTimerQueueHead);
|
|||
|
InsertTailList( &s_RxTimerQueueHead,&pWorkItem->WorkQueueItem.List);
|
|||
|
|
|||
|
KeReleaseSpinLock( &s_RxTimerLock, Irql );
|
|||
|
|
|||
|
if (StartTimer) {
|
|||
|
KeSetTimer( &s_RxTimer, s_RxTimerInterval, &s_RxTimerDpc );
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RxPostRecurrentTimerRequest(
|
|||
|
IN PRDBSS_DEVICE_OBJECT pDeviceObject,
|
|||
|
IN PRX_WORKERTHREAD_ROUTINE Routine,
|
|||
|
IN PVOID pContext,
|
|||
|
IN LARGE_INTEGER TimeInterval)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to post a recurrent timer request. The passed in routine once every
|
|||
|
(TimeInterval) milli seconds.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pDeviceObject - the device object
|
|||
|
|
|||
|
Routine - the routine to be invoked on timeout
|
|||
|
|
|||
|
pContext - the Context parameter that is passed to the driver routine.
|
|||
|
|
|||
|
TimeInterval - the time interval in 100ns ticks.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value indicates whether or not the timer was initialized.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PRX_RECURRENT_WORK_ITEM pRecurrentWorkItem;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
// Allocate a work item.
|
|||
|
pRecurrentWorkItem = (PRX_RECURRENT_WORK_ITEM)
|
|||
|
RxAllocatePoolWithTag(
|
|||
|
NonPagedPool,
|
|||
|
sizeof(RX_RECURRENT_WORK_ITEM),
|
|||
|
RX_TIMER_POOLTAG);
|
|||
|
|
|||
|
if (pRecurrentWorkItem != NULL) {
|
|||
|
InsertTailList(
|
|||
|
&s_RxRecurrentWorkItemsList,
|
|||
|
&pRecurrentWorkItem->RecurrentWorkItemsList);
|
|||
|
|
|||
|
pRecurrentWorkItem->Routine = Routine;
|
|||
|
pRecurrentWorkItem->pContext = pContext;
|
|||
|
pRecurrentWorkItem->TimeInterval = TimeInterval;
|
|||
|
pRecurrentWorkItem->WorkItem.WorkQueueItem.pDeviceObject = pDeviceObject;
|
|||
|
|
|||
|
Status = RxPostOneShotTimerRequest(
|
|||
|
pRecurrentWorkItem->WorkItem.WorkQueueItem.pDeviceObject,
|
|||
|
&pRecurrentWorkItem->WorkItem,
|
|||
|
RxRecurrentTimerWorkItemDispatcher,
|
|||
|
pRecurrentWorkItem,
|
|||
|
TimeInterval);
|
|||
|
} else {
|
|||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RxCancelTimerRequest(
|
|||
|
IN PRDBSS_DEVICE_OBJECT pDeviceObject,
|
|||
|
IN PRX_WORKERTHREAD_ROUTINE Routine,
|
|||
|
IN PVOID pContext)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine cancels a timer request. The request to be cancelled is identified
|
|||
|
by the routine and context.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Routine - the routine to be invoked on timeout
|
|||
|
|
|||
|
pContext - the Context parameter that is passed to the driver routine.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_NOT_FOUND;
|
|||
|
PLIST_ENTRY pListEntry;
|
|||
|
PWORK_QUEUE_ITEM pWorkQueueItem;
|
|||
|
PRX_WORK_ITEM pWorkItem;
|
|||
|
PRX_RECURRENT_WORK_ITEM pRecurrentWorkItem = NULL;
|
|||
|
KIRQL Irql;
|
|||
|
|
|||
|
KeAcquireSpinLock( &s_RxTimerLock, &Irql );
|
|||
|
|
|||
|
// Walk through the list of entries
|
|||
|
for (pListEntry = s_RxTimerQueueHead.Flink;
|
|||
|
(pListEntry != &s_RxTimerQueueHead);
|
|||
|
pListEntry = pListEntry->Flink ) {
|
|||
|
pWorkQueueItem = CONTAINING_RECORD( pListEntry, WORK_QUEUE_ITEM, List );
|
|||
|
pWorkItem = CONTAINING_RECORD( pWorkQueueItem, RX_WORK_ITEM, WorkQueueItem);
|
|||
|
|
|||
|
if ((pWorkItem->WorkQueueItem.pDeviceObject == pDeviceObject) &&
|
|||
|
(pWorkItem->WorkQueueItem.WorkerRoutine == Routine) &&
|
|||
|
(pWorkItem->WorkQueueItem.Parameter == pContext)) {
|
|||
|
RemoveEntryList(pListEntry);
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
pRecurrentWorkItem = NULL;
|
|||
|
break;
|
|||
|
} else if (pWorkItem->WorkQueueItem.WorkerRoutine == RxRecurrentTimerWorkItemDispatcher) {
|
|||
|
pRecurrentWorkItem = (PRX_RECURRENT_WORK_ITEM)pWorkItem->WorkQueueItem.Parameter;
|
|||
|
|
|||
|
if ((pRecurrentWorkItem->Routine == Routine) &&
|
|||
|
(pRecurrentWorkItem->pContext == pContext)) {
|
|||
|
RemoveEntryList(pListEntry);
|
|||
|
RemoveEntryList(&pRecurrentWorkItem->RecurrentWorkItemsList);
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
} else {
|
|||
|
pRecurrentWorkItem = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
KeReleaseSpinLock( &s_RxTimerLock, Irql );
|
|||
|
|
|||
|
if (pRecurrentWorkItem != NULL) {
|
|||
|
RxFreePool(pRecurrentWorkItem);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
RxRecurrentTimerWorkItemDispatcher (
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine dispatches a recurrent timer request. On completion of the invocation of the
|
|||
|
associated routine the request s requeued.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Routine - the routine to be invoked on timeout
|
|||
|
|
|||
|
pContext - the Context parameter that is passed to the driver routine.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PRX_RECURRENT_WORK_ITEM pPeriodicWorkItem = (PRX_RECURRENT_WORK_ITEM)Context;
|
|||
|
PRX_WORKERTHREAD_ROUTINE Routine = pPeriodicWorkItem->Routine;
|
|||
|
PVOID pContext = pPeriodicWorkItem->pContext;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//KIRQL Irql;
|
|||
|
|
|||
|
// Invoke the routine.
|
|||
|
Routine(pContext);
|
|||
|
|
|||
|
// enqueue the item if necessary.
|
|||
|
RxPostOneShotTimerRequest(
|
|||
|
pPeriodicWorkItem->WorkItem.WorkQueueItem.pDeviceObject,
|
|||
|
&pPeriodicWorkItem->WorkItem,
|
|||
|
RxRecurrentTimerWorkItemDispatcher,
|
|||
|
pPeriodicWorkItem,
|
|||
|
pPeriodicWorkItem->TimeInterval);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|