You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
379 lines
9.5 KiB
379 lines
9.5 KiB
|
|
/*
|
|
* 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;
|
|
}
|