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
|
||
|
||
}
|