|
|
/*
* Worker thread functions * * */
#include "pch.h"
KSPIN_LOCK ACPIWorkerSpinLock; WORK_QUEUE_ITEM ACPIWorkItem; LIST_ENTRY ACPIDeviceWorkQueue; BOOLEAN ACPIWorkerBusy;
KEVENT ACPIWorkToDoEvent; KEVENT ACPITerminateEvent; LIST_ENTRY ACPIWorkQueue; HANDLE ACPIThread;
VOID ACPIWorkerThread ( IN PVOID Context );
VOID ACPIWorker( IN PVOID StartContext );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, ACPIInitializeWorker)
#endif
VOID ACPIInitializeWorker ( VOID ) { NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE ThreadHandle; PETHREAD *Thread;
KeInitializeSpinLock (&ACPIWorkerSpinLock); ExInitializeWorkItem (&ACPIWorkItem, ACPIWorkerThread, NULL); InitializeListHead (&ACPIDeviceWorkQueue);
//
// Initialize the ACPI worker thread. This thread is for use by the AML
// interpreter and must not page-fault or have its stack swapped.
//
KeInitializeEvent(&ACPIWorkToDoEvent, NotificationEvent, FALSE); KeInitializeEvent(&ACPITerminateEvent, NotificationEvent, FALSE); InitializeListHead(&ACPIWorkQueue);
//
// Create the worker thread
//
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL); Status = PsCreateSystemThread(&ThreadHandle, THREAD_ALL_ACCESS, &ObjectAttributes, 0, NULL, ACPIWorker, NULL); if (Status != STATUS_SUCCESS) {
ACPIInternalError( ACPI_WORKER );
}
Status = ObReferenceObjectByHandle (ThreadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID *)&Thread, NULL);
if (Status != STATUS_SUCCESS) {
ACPIInternalError( ACPI_WORKER );
} }
VOID ACPISetDeviceWorker ( IN PDEVICE_EXTENSION DevExt, IN ULONG Events ) { BOOLEAN QueueWorker; KIRQL OldIrql;
//
// Synchronize with worker thread
//
KeAcquireSpinLock (&ACPIWorkerSpinLock, &OldIrql); QueueWorker = FALSE;
//
// Set the devices pending events
//
DevExt->WorkQueue.PendingEvents |= Events;
//
// If this device is not being processed, start now
//
if (!DevExt->WorkQueue.Link.Flink) { //
// Queue to worker thread
//
InsertTailList (&ACPIDeviceWorkQueue, &DevExt->WorkQueue.Link); QueueWorker = !ACPIWorkerBusy; ACPIWorkerBusy = TRUE; }
//
// Drop lock, and if needed get a worker thread
//
KeReleaseSpinLock (&ACPIWorkerSpinLock, OldIrql); if (QueueWorker) { ExQueueWorkItem (&ACPIWorkItem, DelayedWorkQueue); } }
VOID ACPIWorkerThread ( IN PVOID Context ) { KIRQL OldIrql; PDEVICE_EXTENSION DevExt; ULONG Events; PLIST_ENTRY Link;
KeAcquireSpinLock (&ACPIWorkerSpinLock, &OldIrql); ACPIWorkerBusy = TRUE;
//
// Loop and handle each queue device
//
while (!IsListEmpty(&ACPIDeviceWorkQueue)) { Link = ACPIDeviceWorkQueue.Flink; RemoveEntryList (Link); Link->Flink = NULL;
DevExt = CONTAINING_RECORD (Link, DEVICE_EXTENSION, WorkQueue.Link);
//
// Dispatch the pending events
//
Events = DevExt->WorkQueue.PendingEvents; DevExt->WorkQueue.PendingEvents = 0;
KeReleaseSpinLock (&ACPIWorkerSpinLock, OldIrql); DevExt->DispatchTable->Worker (DevExt, Events); KeAcquireSpinLock (&ACPIWorkerSpinLock, &OldIrql); }
ACPIWorkerBusy = FALSE; KeReleaseSpinLock (&ACPIWorkerSpinLock, OldIrql); }
#if DBG
EXCEPTION_DISPOSITION ACPIWorkerThreadFilter( IN PWORKER_THREAD_ROUTINE WorkerRoutine, IN PVOID Parameter, IN PEXCEPTION_POINTERS ExceptionInfo ) { KdPrint(("ACPIWORKER: exception in worker routine %lx(%lx)\n", WorkerRoutine, Parameter)); KdPrint((" exception record at %lx\n", ExceptionInfo->ExceptionRecord)); KdPrint((" context record at %lx\n",ExceptionInfo->ContextRecord));
try { DbgBreakPoint();
} except (EXCEPTION_EXECUTE_HANDLER) { //
// No kernel debugger attached, so let the system thread
// exception handler call KeBugCheckEx.
//
return(EXCEPTION_CONTINUE_SEARCH); }
return(EXCEPTION_EXECUTE_HANDLER); } #endif
typedef enum _ACPI_WORKER_OBJECT { ACPIWorkToDo, ACPITerminate, ACPIMaximumObject } ACPI_WORKER_OBJECT;
VOID ACPIWorker( IN PVOID StartContext ) { PLIST_ENTRY Entry; WORK_QUEUE_TYPE QueueType; PWORK_QUEUE_ITEM WorkItem; KIRQL OldIrql; NTSTATUS Status; static KWAIT_BLOCK WaitBlockArray[ACPIMaximumObject]; PVOID WaitObjects[ACPIMaximumObject];
ACPIThread = PsGetCurrentThread ();
//
// Wait for the modified page writer event AND the PFN mutex.
//
WaitObjects[ACPIWorkToDo] = (PVOID)&ACPIWorkToDoEvent; WaitObjects[ACPITerminate] = (PVOID)&ACPITerminateEvent;
//
// Loop forever waiting for a work queue item, calling the processing
// routine, and then waiting for another work queue item.
//
do {
//
// Wait until something is put in the queue.
//
// By specifying a wait mode of KernelMode, the thread's kernel stack is
// not swappable
//
Status = KeWaitForMultipleObjects(ACPIMaximumObject, &WaitObjects[0], WaitAny, Executive, KernelMode, FALSE, NULL, &WaitBlockArray[0]);
//
// Switch on the wait status.
//
switch (Status) {
case ACPIWorkToDo: break;
case ACPITerminate: // Stephane - you need to clear out any pending requests,
// wake people up, etc. here.
//
// Also make sure you free up any allocated pool, etc.
PsTerminateSystemThread (STATUS_SUCCESS); break;
default: break; }
KeAcquireSpinLock(&ACPIWorkerSpinLock, &OldIrql); ASSERT(!IsListEmpty(&ACPIWorkQueue)); Entry = RemoveHeadList(&ACPIWorkQueue);
if (IsListEmpty(&ACPIWorkQueue)) { KeClearEvent(&ACPIWorkToDoEvent); } KeReleaseSpinLock(&ACPIWorkerSpinLock, OldIrql);
WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List);
//
// Execute the specified routine.
//
#if DBG
try {
PVOID WorkerRoutine; PVOID Parameter;
WorkerRoutine = WorkItem->WorkerRoutine; Parameter = WorkItem->Parameter; (WorkItem->WorkerRoutine)(WorkItem->Parameter); if (KeGetCurrentIrql() != 0) {
ACPIPrint( ( ACPI_PRINT_CRITICAL, "ACPIWORKER: worker exit at IRQL %d, worker routine %x, " "parameter %x, item %x\n", KeGetCurrentIrql(), WorkerRoutine, Parameter, WorkItem ) ); DbgBreakPoint();
}
} except( ACPIWorkerThreadFilter(WorkItem->WorkerRoutine, WorkItem->Parameter, GetExceptionInformation() )) { }
#else
(WorkItem->WorkerRoutine)(WorkItem->Parameter); if (KeGetCurrentIrql() != 0) { KeBugCheckEx( IRQL_NOT_LESS_OR_EQUAL, (ULONG_PTR)WorkItem->WorkerRoutine, (ULONG_PTR)KeGetCurrentIrql(), (ULONG_PTR)WorkItem->WorkerRoutine, (ULONG_PTR)WorkItem ); } #endif
} while(TRUE); }
VOID OSQueueWorkItem( IN PWORK_QUEUE_ITEM WorkItem )
/*++
Routine Description:
This function inserts a work item into a work queue that is processed by the ACPI worker thread
Arguments:
WorkItem - Supplies a pointer to the work item to add the the queue. This structure must be located in NonPagedPool. The work item structure contains a doubly linked list entry, the address of a routine to call and a parameter to pass to that routine.
Return Value:
None
--*/
{ KIRQL OldIrql;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//
// Insert the work item
//
KeAcquireSpinLock(&ACPIWorkerSpinLock, &OldIrql); if (IsListEmpty(&ACPIWorkQueue)) { KeSetEvent(&ACPIWorkToDoEvent, 0, FALSE); } InsertTailList(&ACPIWorkQueue, &WorkItem->List); KeReleaseSpinLock(&ACPIWorkerSpinLock, OldIrql); return; }
|