495 lines
10 KiB
C
495 lines
10 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
dpcobj.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the kernel DPC object. Functions are provided
|
|||
|
to initialize, insert, and remove DPC objects.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David N. Cutler (davec) 6-Mar-1989
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ki.h"
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, KeFlushQueuedDpcs)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// The following assert macro is used to check that an input dpc is
|
|||
|
// really a kdpc and not something else, like deallocated pool.
|
|||
|
//
|
|||
|
|
|||
|
#define ASSERT_DPC(E) { \
|
|||
|
ASSERT((E)->Type == DpcObject); \
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KeInitializeDpc (
|
|||
|
IN PRKDPC Dpc,
|
|||
|
IN PKDEFERRED_ROUTINE DeferredRoutine,
|
|||
|
IN PVOID DeferredContext
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function initializes a kernel DPC object. The deferred routine
|
|||
|
and context parameter are stored in the DPC object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Dpc - Supplies a pointer to a control object of type DPC.
|
|||
|
|
|||
|
DeferredRoutine - Supplies a pointer to a function that is called when
|
|||
|
the DPC object is removed from the current processor's DPC queue.
|
|||
|
|
|||
|
DeferredContext - Supplies a pointer to an arbitrary data structure which is
|
|||
|
to be passed to the function specified by the DeferredRoutine parameter.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// Initialize standard control object header.
|
|||
|
//
|
|||
|
|
|||
|
Dpc->Type = DpcObject;
|
|||
|
Dpc->Number = 0;
|
|||
|
Dpc->Importance = MediumImportance;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize deferred routine address and deferred context parameter.
|
|||
|
//
|
|||
|
|
|||
|
Dpc->DeferredRoutine = DeferredRoutine;
|
|||
|
Dpc->DeferredContext = DeferredContext;
|
|||
|
Dpc->Lock = NULL;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
KeInsertQueueDpc (
|
|||
|
IN PRKDPC Dpc,
|
|||
|
IN PVOID SystemArgument1,
|
|||
|
IN PVOID SystemArgument2
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function inserts a DPC object into the DPC queue. If the DPC object
|
|||
|
is already in the DPC queue, then no operation is performed. Otherwise,
|
|||
|
the DPC object is inserted in the DPC queue and a dispatch interrupt is
|
|||
|
requested.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Dpc - Supplies a pointer to a control object of type DPC.
|
|||
|
|
|||
|
SystemArgument1, SystemArgument2 - Supply a set of two arguments that
|
|||
|
contain untyped data provided by the executive.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
If the DPC object is already in a DPC queue, then a value of FALSE is
|
|||
|
returned. Otherwise a value of TRUE is returned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
ULONG Index;
|
|||
|
PKSPIN_LOCK Lock;
|
|||
|
KIRQL OldIrql;
|
|||
|
PKPRCB Prcb;
|
|||
|
ULONG Processor;
|
|||
|
|
|||
|
ASSERT_DPC(Dpc);
|
|||
|
|
|||
|
//
|
|||
|
// Disable interrupts.
|
|||
|
//
|
|||
|
|
|||
|
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// Acquire the DPC queue lock for the specified target processor.
|
|||
|
//
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
if (Dpc->Number >= MAXIMUM_PROCESSORS) {
|
|||
|
Processor = Dpc->Number - MAXIMUM_PROCESSORS;
|
|||
|
Prcb = KiProcessorBlock[Processor];
|
|||
|
|
|||
|
} else {
|
|||
|
Prcb = KeGetCurrentPrcb();
|
|||
|
}
|
|||
|
|
|||
|
KiAcquireSpinLock(&Prcb->DpcLock);
|
|||
|
|
|||
|
#else
|
|||
|
|
|||
|
Prcb = KeGetCurrentPrcb();
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// If the DPC object is not in a DPC queue, then store the system
|
|||
|
// arguments, insert the DPC object in the DPC queue, increment the
|
|||
|
// number of DPCs queued to the target processor, increment the DPC
|
|||
|
// queue depth, set the address of the DPC target DPC spinlock, and
|
|||
|
// request a dispatch interrupt if appropriate.
|
|||
|
//
|
|||
|
|
|||
|
if ((Lock = InterlockedCompareExchangePointer(&Dpc->Lock, &Prcb->DpcLock, NULL)) == NULL) {
|
|||
|
Prcb->DpcCount += 1;
|
|||
|
Prcb->DpcQueueDepth += 1;
|
|||
|
Dpc->SystemArgument1 = SystemArgument1;
|
|||
|
Dpc->SystemArgument2 = SystemArgument2;
|
|||
|
|
|||
|
//
|
|||
|
// If the DPC is of high importance, then insert the DPC at the
|
|||
|
// head of the DPC queue. Otherwise, insert the DPC at the end
|
|||
|
// of the DPC queue.
|
|||
|
//
|
|||
|
|
|||
|
if (Dpc->Importance == HighImportance) {
|
|||
|
InsertHeadList(&Prcb->DpcListHead, &Dpc->DpcListEntry);
|
|||
|
|
|||
|
} else {
|
|||
|
InsertTailList(&Prcb->DpcListHead, &Dpc->DpcListEntry);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If a DPC routine is not active on the target processor, then
|
|||
|
// request a dispatch interrupt if appropriate.
|
|||
|
//
|
|||
|
|
|||
|
if ((Prcb->DpcRoutineActive == FALSE) &&
|
|||
|
(Prcb->DpcInterruptRequested == FALSE)) {
|
|||
|
|
|||
|
//
|
|||
|
// Request a dispatch interrupt on the current processor if
|
|||
|
// the DPC is not of low importance, the length of the DPC
|
|||
|
// queue has exceeded the maximum threshold, or if the DPC
|
|||
|
// request rate is below the minimum threshold.
|
|||
|
//
|
|||
|
|
|||
|
#if defined(NT_UP)
|
|||
|
|
|||
|
if ((Dpc->Importance != LowImportance) ||
|
|||
|
(Prcb->DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) ||
|
|||
|
(Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) {
|
|||
|
Prcb->DpcInterruptRequested = TRUE;
|
|||
|
KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the DPC is being queued to another processor and the
|
|||
|
// DPC is of high importance, or the length of the other
|
|||
|
// processor's DPC queue has exceeded the maximum threshold,
|
|||
|
// then request a dispatch interrupt.
|
|||
|
//
|
|||
|
|
|||
|
#else
|
|||
|
|
|||
|
if (Prcb != KeGetCurrentPrcb()) {
|
|||
|
if (((Dpc->Importance == HighImportance) ||
|
|||
|
(Prcb->DpcQueueDepth >= Prcb->MaximumDpcQueueDepth))) {
|
|||
|
Prcb->DpcInterruptRequested = TRUE;
|
|||
|
KiIpiSend(AFFINITY_MASK(Processor), IPI_DPC);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Request a dispatch interrupt on the current processor if
|
|||
|
// the DPC is not of low importance, the length of the DPC
|
|||
|
// queue has exceeded the maximum threshold, or if the DPC
|
|||
|
// request rate is below the minimum threshold.
|
|||
|
//
|
|||
|
|
|||
|
if ((Dpc->Importance != LowImportance) ||
|
|||
|
(Prcb->DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) ||
|
|||
|
(Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) {
|
|||
|
Prcb->DpcInterruptRequested = TRUE;
|
|||
|
KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Release the DPC lock, enable interrupts, and return whether the
|
|||
|
// DPC was queued or not.
|
|||
|
//
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
KiReleaseSpinLock(&Prcb->DpcLock);
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
KeLowerIrql(OldIrql);
|
|||
|
return (Lock == NULL);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
KeRemoveQueueDpc (
|
|||
|
IN PRKDPC Dpc
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function removes a DPC object from the DPC queue. If the DPC object
|
|||
|
is not in the DPC queue, then no operation is performed. Otherwise, the
|
|||
|
DPC object is removed from the DPC queue and its inserted state is set
|
|||
|
FALSE.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Dpc - Supplies a pointer to a control object of type DPC.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
If the DPC object is not in the DPC queue, then a value of FALSE is
|
|||
|
returned. Otherwise a value of TRUE is returned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PKSPIN_LOCK Lock;
|
|||
|
PKPRCB Prcb;
|
|||
|
|
|||
|
ASSERT_DPC(Dpc);
|
|||
|
|
|||
|
//
|
|||
|
// If the DPC object is in the DPC queue, then remove it from the queue
|
|||
|
// and set its inserted state to FALSE.
|
|||
|
//
|
|||
|
|
|||
|
_disable();
|
|||
|
Lock = Dpc->Lock;
|
|||
|
if (Lock != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Acquire the DPC lock of the target processor.
|
|||
|
//
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
KiAcquireSpinLock(Lock);
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// If the specified DPC is still in the DPC queue, then remove
|
|||
|
// it.
|
|||
|
//
|
|||
|
// N.B. It is possible for specified DPC to be removed from the
|
|||
|
// specified DPC queue before the DPC lock is obtained.
|
|||
|
//
|
|||
|
//
|
|||
|
|
|||
|
if (Lock == Dpc->Lock) {
|
|||
|
Prcb = CONTAINING_RECORD(Lock, KPRCB, DpcLock);
|
|||
|
Prcb->DpcQueueDepth -= 1;
|
|||
|
RemoveEntryList(&Dpc->DpcListEntry);
|
|||
|
Dpc->Lock = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Release the DPC lock of the target processor.
|
|||
|
//
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
KiReleaseSpinLock(Lock);
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Enable interrupts and return whether the DPC was removed from a DPC
|
|||
|
// queue.
|
|||
|
//
|
|||
|
|
|||
|
_enable();
|
|||
|
return (Lock != NULL);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KeSetImportanceDpc (
|
|||
|
IN PRKDPC Dpc,
|
|||
|
IN KDPC_IMPORTANCE Importance
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function sets the importance of a DPC.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Dpc - Supplies a pointer to a control object of type DPC.
|
|||
|
|
|||
|
Number - Supplies the importance of the DPC.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// Set the importance of the DPC.
|
|||
|
//
|
|||
|
|
|||
|
Dpc->Importance = (UCHAR)Importance;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KeSetTargetProcessorDpc (
|
|||
|
IN PRKDPC Dpc,
|
|||
|
IN CCHAR Number
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function sets the processor number to which the DPC is targeted.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Dpc - Supplies a pointer to a control object of type DPC.
|
|||
|
|
|||
|
Number - Supplies the target processor number.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// Set target processor number.
|
|||
|
//
|
|||
|
// The target processor number if biased by the maximum number of
|
|||
|
// processors that are supported.
|
|||
|
//
|
|||
|
|
|||
|
Dpc->Number = MAXIMUM_PROCESSORS + Number;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KeFlushQueuedDpcs(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function causes all current DPCs on all processors to execute to completion.
|
|||
|
This is used at driver unload to make sure all driver DPC processing has exited the
|
|||
|
driver image before the code and data is deleted
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
PKTHREAD CurrentThread;
|
|||
|
KPRIORITY OldPriority;
|
|||
|
KAFFINITY ProcessorMask;
|
|||
|
BOOLEAN SetAffinity;
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
PAGED_CODE ();
|
|||
|
|
|||
|
#if !defined(NT_UP)
|
|||
|
|
|||
|
//
|
|||
|
// Set the priority of this thread high so we will always run on the target processor
|
|||
|
//
|
|||
|
|
|||
|
CurrentThread = KeGetCurrentThread();
|
|||
|
OldPriority = KeSetPriorityThread(CurrentThread, HIGH_PRIORITY);
|
|||
|
ProcessorMask = KeActiveProcessors;
|
|||
|
SetAffinity = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Subtract the processor we are running on now from the affinity mask and let the
|
|||
|
// kernel select the next processor we can run on.
|
|||
|
//
|
|||
|
|
|||
|
ProcessorMask &= ~(AFFINITY_MASK(KeGetCurrentProcessorNumber()));
|
|||
|
while (ProcessorMask) {
|
|||
|
KeSetSystemAffinityThread(ProcessorMask);
|
|||
|
SetAffinity = TRUE;
|
|||
|
ProcessorMask &= ~(AFFINITY_MASK(KeGetCurrentProcessorNumber()));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Put affinity and priority back as it was
|
|||
|
//
|
|||
|
|
|||
|
if (SetAffinity) {
|
|||
|
KeRevertToUserAffinityThread ();
|
|||
|
}
|
|||
|
|
|||
|
OldPriority = KeSetPriorityThread(CurrentThread, OldPriority);
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
}
|