windows-nt/Source/XPSP1/NT/base/fs/rdr2/rxce/rxtimer.c
2020-09-26 16:20:57 +08:00

507 lines
13 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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);
}