Files
nt-lite/code/kernel/ke_dpc.c
T

105 lines
2.9 KiB
C

// ke_dpc.c - deferred procedure calls
#include <ke_dpc.h>
#include <ke_irql.h>
#include <ke_debug.h>
#include <intrin.h>
static LIST_ENTRY gDpcQueue;
static SPINLOCK gDpcLock;
static volatile long gDpcInitialized = 0;
static volatile long gDpcActive = 0;
void KeInitializeDpcSystem(void) {
LIST_INIT(&gDpcQueue);
gDpcLock = (SPINLOCK)SPINLOCK_INIT;
gDpcActive = 0;
gDpcInitialized = 1;
}
static void KiEnsureDpcInitialized(void) {
if (gDpcInitialized)
return;
// Single-CPU bring-up path: this is only a safety net for early callers.
KeInitializeDpcSystem();
}
__declspec(dllexport) void KeInitializeDpc(KDPC* dpc,
KDEFERRED_ROUTINE routine,
void* context) {
if (!dpc)
return;
LIST_INIT(&dpc->Link);
dpc->Routine = routine;
dpc->Context = context;
dpc->SystemArgument1 = (void*)0;
dpc->SystemArgument2 = (void*)0;
dpc->Inserted = 0;
}
__declspec(dllexport) bool KeInsertQueueDpc(KDPC* dpc,
void* systemArgument1,
void* systemArgument2) {
if (!dpc || !dpc->Routine)
return false;
KiEnsureDpcInitialized();
// Coalesce repeated inserts of the same KDPC. The first insert owns the
// argument pair until the DPC is popped by the dispatcher.
if (_InterlockedCompareExchange(&dpc->Inserted, 1, 0) != 0)
return false;
dpc->SystemArgument1 = systemArgument1;
dpc->SystemArgument2 = systemArgument2;
uintptr_t flags = KeAcquireSpinlock(&gDpcLock);
ListInsertTail(&gDpcQueue, &dpc->Link);
KeReleaseSpinlock(&gDpcLock, flags);
return true;
}
void KiDrainDpcQueue(void) {
if (!gDpcInitialized)
return;
// Never run DPC routines above DISPATCH_LEVEL. If a future nested IRQ path
// reaches here at DIRQL, leave the work pending for the outermost exit.
if (KeGetCurrentIrql() > DISPATCH_LEVEL)
return;
if (_InterlockedCompareExchange(&gDpcActive, 1, 0) != 0)
return;
KIRQL oldIrql = KeGetCurrentIrql();
if (oldIrql < DISPATCH_LEVEL)
KeRaiseIrql(DISPATCH_LEVEL);
for (;;) {
uintptr_t flags = KeAcquireSpinlock(&gDpcLock);
LIST_ENTRY* node = ListPopHead(&gDpcQueue);
if (!node) {
KeReleaseSpinlock(&gDpcLock, flags);
break;
}
KDPC* dpc = CONTAINING_RECORD(node, KDPC, Link);
void* arg1 = dpc->SystemArgument1;
void* arg2 = dpc->SystemArgument2;
dpc->SystemArgument1 = (void*)0;
dpc->SystemArgument2 = (void*)0;
dpc->Inserted = 0;
KeReleaseSpinlock(&gDpcLock, flags);
KeAssertIrql(DISPATCH_LEVEL);
dpc->Routine(dpc, dpc->Context, arg1, arg2);
KeAssertIrql(DISPATCH_LEVEL);
}
gDpcActive = 0;
if (oldIrql < DISPATCH_LEVEL)
KeLowerIrql(oldIrql);
}