implement proper IRQL, DPCs, kernel and user APCs, alertable waits, async file I/O with cancellation, trap frames, selftests, a real driver/device model, native async FAT32, VFAT dotfile fixes, syscall ABI cleanup, async_apc and async_cancel tests, POSIX multi-volume paths, and pimp the readme.
This commit is contained in:
1 parent
f5fd7bdd75
commit
529f816166
75 files changed
+9969
-5675
No files matched your search
@@ -0,0 +1,104 @@
|
||||
// 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);
|
||||
}
|
||||
Reference in new issue
Block a user