// Kernel passive-level work queue. This file owns the generic mechanism used // to move ISR/DPC-originated work to a sleepable system thread context. #include #include #include #include #include #include #include #include #include #include #include #include #include static SPINLOCK gWorkQueueLock = SPINLOCK_INIT; static LIST_ENTRY gWorkQueueList; static EVENT* gWorkQueueEvent; static KDPC gWorkQueueWakeDpc; static volatile int32_t gWorkQueueListReady; static volatile int32_t gWorkQueueStartState; // 0=not started, 1=starting, 2=started. // Signals the passive worker after an enqueue from hardware interrupt context. static void KiWorkQueueWakeDpc(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2) { (void)dpc; (void)context; (void)systemArgument1; (void)systemArgument2; KeAssertIrql(DISPATCH_LEVEL); EVENT* event = gWorkQueueEvent; if (event) KeSignalEvent(event); } static void KiWorkQueueWorker(void* arg) { (void)arg; KeAssertIrql(PASSIVE_LEVEL); while (true) { (void)KeWaitForEvent(gWorkQueueEvent, 0); while (true) { KWORK_ITEM* item = (KWORK_ITEM*)0; uintptr_t flags = KeAcquireSpinlock(&gWorkQueueLock); if (!LIST_IS_EMPTY(&gWorkQueueList)) { LIST_ENTRY* node = KeListPopHeadChecked(&gWorkQueueList); item = CONTAINING_RECORD(node, KWORK_ITEM, Link); } KeReleaseSpinlock(&gWorkQueueLock, flags); if (!item) break; // Allow the item to be queued again while it is running. This keeps // level-triggered users such as cursor updates naturally coalesced // but does not lose an update that arrives during the callback. NtlAtomicExchange32(&item->Queued, 0); if (item->Routine) item->Routine(item->Context); } } } void KeInitializeWorkItem(KWORK_ITEM* item, KWORK_ROUTINE routine, void* context) { if (!item) return; LIST_INIT(&item->Link); item->Routine = routine; item->Context = context; item->Queued = 0; } bool KeIsWorkQueueStarted(void) { return NtlAtomicCompareExchange32(&gWorkQueueStartState, 2, 2) == 2; } bool KeQueueWorkItem(KWORK_ITEM* item) { if (!item || !item->Routine) return false; if (NtlAtomicCompareExchange32(&item->Queued, 1, 0) != 0) return false; if (!NtlAtomicCompareExchange32(&gWorkQueueListReady, 1, 1)) { NtlAtomicExchange32(&item->Queued, 0); return false; } uintptr_t flags = KeAcquireSpinlock(&gWorkQueueLock); KeListInsertTailChecked(&gWorkQueueList, &item->Link); EVENT* event = gWorkQueueEvent; KeReleaseSpinlock(&gWorkQueueLock, flags); if (event) { // Event signaling can detach waiters, so callers above passive level // defer the wake until no caller-owned spinlock remains held. if (KeGetCurrentIrql() == PASSIVE_LEVEL) KeSignalEvent(event); else (void)KeInsertQueueDpc(&gWorkQueueWakeDpc, (void*)0, (void*)0); } return true; } KSTATUS KeStartWorkQueue(void) { int32_t state = NtlAtomicCompareExchange32(&gWorkQueueStartState, 1, 0); if (state == 2) return NTLSTATUS_SUCCESS; if (state == 1) { while (NtlAtomicCompareExchange32(&gWorkQueueStartState, 2, 2) == 1) KeYield(); return KeIsWorkQueueStarted() ? NTLSTATUS_SUCCESS : NTLSTATUS_FAILURE; } LIST_INIT(&gWorkQueueList); KeInitializeDpc(&gWorkQueueWakeDpc, KiWorkQueueWakeDpc, (void*)0); NtlAtomicExchange32(&gWorkQueueListReady, 1); KSTATUS s = KeEventCreate(EventAutoReset, 0, &gWorkQueueEvent); if (KSUCCESS(s)) { THREAD* worker = (THREAD*)0; const KE_THREAD_CREATE_PARAMETERS parameters = KE_THREAD_CREATE_NAMED_NORMAL(PriorityNormal, "work.queue"); s = KeCreateKernelThread((uintptr_t)KiWorkQueueWorker, 0, ¶meters, &worker); // Persistent system worker; keep the creator reference for kernel life. (void)worker; } if (KSUCCESS(s)) { NtlAtomicExchange32(&gWorkQueueStartState, 2); KeSignalEvent(gWorkQueueEvent); } else { NtlAtomicExchange32(&gWorkQueueStartState, 0); } return s; }