Leaked source code of windows server 2003
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

  1. /*
  2. * Worker thread functions
  3. *
  4. *
  5. */
  6. #include "pch.h"
  7. KSPIN_LOCK ACPIWorkerSpinLock;
  8. WORK_QUEUE_ITEM ACPIWorkItem;
  9. LIST_ENTRY ACPIDeviceWorkQueue;
  10. BOOLEAN ACPIWorkerBusy;
  11. KEVENT ACPIWorkToDoEvent;
  12. KEVENT ACPITerminateEvent;
  13. LIST_ENTRY ACPIWorkQueue;
  14. HANDLE ACPIThread;
  15. VOID
  16. ACPIWorkerThread (
  17. IN PVOID Context
  18. );
  19. VOID
  20. ACPIWorker(
  21. IN PVOID StartContext
  22. );
  23. #ifdef ALLOC_PRAGMA
  24. #pragma alloc_text(INIT, ACPIInitializeWorker)
  25. #endif
  26. VOID
  27. ACPIInitializeWorker (
  28. VOID
  29. )
  30. {
  31. NTSTATUS Status;
  32. OBJECT_ATTRIBUTES ObjectAttributes;
  33. HANDLE ThreadHandle;
  34. PETHREAD *Thread;
  35. KeInitializeSpinLock (&ACPIWorkerSpinLock);
  36. ExInitializeWorkItem (&ACPIWorkItem, ACPIWorkerThread, NULL);
  37. InitializeListHead (&ACPIDeviceWorkQueue);
  38. //
  39. // Initialize the ACPI worker thread. This thread is for use by the AML
  40. // interpreter and must not page-fault or have its stack swapped.
  41. //
  42. KeInitializeEvent(&ACPIWorkToDoEvent, NotificationEvent, FALSE);
  43. KeInitializeEvent(&ACPITerminateEvent, NotificationEvent, FALSE);
  44. InitializeListHead(&ACPIWorkQueue);
  45. //
  46. // Create the worker thread
  47. //
  48. InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
  49. Status = PsCreateSystemThread(&ThreadHandle,
  50. THREAD_ALL_ACCESS,
  51. &ObjectAttributes,
  52. 0,
  53. NULL,
  54. ACPIWorker,
  55. NULL);
  56. if (Status != STATUS_SUCCESS) {
  57. ACPIInternalError( ACPI_WORKER );
  58. }
  59. Status = ObReferenceObjectByHandle (ThreadHandle,
  60. THREAD_ALL_ACCESS,
  61. NULL,
  62. KernelMode,
  63. (PVOID *)&Thread,
  64. NULL);
  65. if (Status != STATUS_SUCCESS) {
  66. ACPIInternalError( ACPI_WORKER );
  67. }
  68. }
  69. VOID
  70. ACPISetDeviceWorker (
  71. IN PDEVICE_EXTENSION DevExt,
  72. IN ULONG Events
  73. )
  74. {
  75. BOOLEAN QueueWorker;
  76. KIRQL OldIrql;
  77. //
  78. // Synchronize with worker thread
  79. //
  80. KeAcquireSpinLock (&ACPIWorkerSpinLock, &OldIrql);
  81. QueueWorker = FALSE;
  82. //
  83. // Set the devices pending events
  84. //
  85. DevExt->WorkQueue.PendingEvents |= Events;
  86. //
  87. // If this device is not being processed, start now
  88. //
  89. if (!DevExt->WorkQueue.Link.Flink) {
  90. //
  91. // Queue to worker thread
  92. //
  93. InsertTailList (&ACPIDeviceWorkQueue, &DevExt->WorkQueue.Link);
  94. QueueWorker = !ACPIWorkerBusy;
  95. ACPIWorkerBusy = TRUE;
  96. }
  97. //
  98. // Drop lock, and if needed get a worker thread
  99. //
  100. KeReleaseSpinLock (&ACPIWorkerSpinLock, OldIrql);
  101. if (QueueWorker) {
  102. ExQueueWorkItem (&ACPIWorkItem, DelayedWorkQueue);
  103. }
  104. }
  105. VOID
  106. ACPIWorkerThread (
  107. IN PVOID Context
  108. )
  109. {
  110. KIRQL OldIrql;
  111. PDEVICE_EXTENSION DevExt;
  112. ULONG Events;
  113. PLIST_ENTRY Link;
  114. KeAcquireSpinLock (&ACPIWorkerSpinLock, &OldIrql);
  115. ACPIWorkerBusy = TRUE;
  116. //
  117. // Loop and handle each queue device
  118. //
  119. while (!IsListEmpty(&ACPIDeviceWorkQueue)) {
  120. Link = ACPIDeviceWorkQueue.Flink;
  121. RemoveEntryList (Link);
  122. Link->Flink = NULL;
  123. DevExt = CONTAINING_RECORD (Link, DEVICE_EXTENSION, WorkQueue.Link);
  124. //
  125. // Dispatch the pending events
  126. //
  127. Events = DevExt->WorkQueue.PendingEvents;
  128. DevExt->WorkQueue.PendingEvents = 0;
  129. KeReleaseSpinLock (&ACPIWorkerSpinLock, OldIrql);
  130. DevExt->DispatchTable->Worker (DevExt, Events);
  131. KeAcquireSpinLock (&ACPIWorkerSpinLock, &OldIrql);
  132. }
  133. ACPIWorkerBusy = FALSE;
  134. KeReleaseSpinLock (&ACPIWorkerSpinLock, OldIrql);
  135. }
  136. #if DBG
  137. EXCEPTION_DISPOSITION
  138. ACPIWorkerThreadFilter(
  139. IN PWORKER_THREAD_ROUTINE WorkerRoutine,
  140. IN PVOID Parameter,
  141. IN PEXCEPTION_POINTERS ExceptionInfo
  142. )
  143. {
  144. KdPrint(("ACPIWORKER: exception in worker routine %lx(%lx)\n", WorkerRoutine, Parameter));
  145. KdPrint((" exception record at %lx\n", ExceptionInfo->ExceptionRecord));
  146. KdPrint((" context record at %lx\n",ExceptionInfo->ContextRecord));
  147. try {
  148. DbgBreakPoint();
  149. } except (EXCEPTION_EXECUTE_HANDLER) {
  150. //
  151. // No kernel debugger attached, so let the system thread
  152. // exception handler call KeBugCheckEx.
  153. //
  154. return(EXCEPTION_CONTINUE_SEARCH);
  155. }
  156. return(EXCEPTION_EXECUTE_HANDLER);
  157. }
  158. #endif
  159. typedef enum _ACPI_WORKER_OBJECT {
  160. ACPIWorkToDo,
  161. ACPITerminate,
  162. ACPIMaximumObject
  163. } ACPI_WORKER_OBJECT;
  164. VOID
  165. ACPIWorker(
  166. IN PVOID StartContext
  167. )
  168. {
  169. PLIST_ENTRY Entry;
  170. WORK_QUEUE_TYPE QueueType;
  171. PWORK_QUEUE_ITEM WorkItem;
  172. KIRQL OldIrql;
  173. NTSTATUS Status;
  174. static KWAIT_BLOCK WaitBlockArray[ACPIMaximumObject];
  175. PVOID WaitObjects[ACPIMaximumObject];
  176. ACPIThread = PsGetCurrentThread ();
  177. //
  178. // Wait for the modified page writer event AND the PFN mutex.
  179. //
  180. WaitObjects[ACPIWorkToDo] = (PVOID)&ACPIWorkToDoEvent;
  181. WaitObjects[ACPITerminate] = (PVOID)&ACPITerminateEvent;
  182. //
  183. // Loop forever waiting for a work queue item, calling the processing
  184. // routine, and then waiting for another work queue item.
  185. //
  186. do {
  187. //
  188. // Wait until something is put in the queue.
  189. //
  190. // By specifying a wait mode of KernelMode, the thread's kernel stack is
  191. // not swappable
  192. //
  193. Status = KeWaitForMultipleObjects(ACPIMaximumObject,
  194. &WaitObjects[0],
  195. WaitAny,
  196. Executive,
  197. KernelMode,
  198. FALSE,
  199. NULL,
  200. &WaitBlockArray[0]);
  201. //
  202. // Switch on the wait status.
  203. //
  204. switch (Status) {
  205. case ACPIWorkToDo:
  206. break;
  207. case ACPITerminate:
  208. // Stephane - you need to clear out any pending requests,
  209. // wake people up, etc. here.
  210. //
  211. // Also make sure you free up any allocated pool, etc.
  212. PsTerminateSystemThread (STATUS_SUCCESS);
  213. break;
  214. default:
  215. break;
  216. }
  217. KeAcquireSpinLock(&ACPIWorkerSpinLock, &OldIrql);
  218. ASSERT(!IsListEmpty(&ACPIWorkQueue));
  219. Entry = RemoveHeadList(&ACPIWorkQueue);
  220. if (IsListEmpty(&ACPIWorkQueue)) {
  221. KeClearEvent(&ACPIWorkToDoEvent);
  222. }
  223. KeReleaseSpinLock(&ACPIWorkerSpinLock, OldIrql);
  224. WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List);
  225. //
  226. // Execute the specified routine.
  227. //
  228. #if DBG
  229. try {
  230. PVOID WorkerRoutine;
  231. PVOID Parameter;
  232. WorkerRoutine = WorkItem->WorkerRoutine;
  233. Parameter = WorkItem->Parameter;
  234. (WorkItem->WorkerRoutine)(WorkItem->Parameter);
  235. if (KeGetCurrentIrql() != 0) {
  236. ACPIPrint( (
  237. ACPI_PRINT_CRITICAL,
  238. "ACPIWORKER: worker exit at IRQL %d, worker routine %x, "
  239. "parameter %x, item %x\n",
  240. KeGetCurrentIrql(),
  241. WorkerRoutine,
  242. Parameter,
  243. WorkItem
  244. ) );
  245. DbgBreakPoint();
  246. }
  247. } except( ACPIWorkerThreadFilter(WorkItem->WorkerRoutine,
  248. WorkItem->Parameter,
  249. GetExceptionInformation() )) {
  250. }
  251. #else
  252. (WorkItem->WorkerRoutine)(WorkItem->Parameter);
  253. if (KeGetCurrentIrql() != 0) {
  254. KeBugCheckEx(
  255. IRQL_NOT_LESS_OR_EQUAL,
  256. (ULONG_PTR)WorkItem->WorkerRoutine,
  257. (ULONG_PTR)KeGetCurrentIrql(),
  258. (ULONG_PTR)WorkItem->WorkerRoutine,
  259. (ULONG_PTR)WorkItem
  260. );
  261. }
  262. #endif
  263. } while(TRUE);
  264. }
  265. VOID
  266. OSQueueWorkItem(
  267. IN PWORK_QUEUE_ITEM WorkItem
  268. )
  269. /*++
  270. Routine Description:
  271. This function inserts a work item into a work queue that is processed
  272. by the ACPI worker thread
  273. Arguments:
  274. WorkItem - Supplies a pointer to the work item to add the the queue.
  275. This structure must be located in NonPagedPool. The work item
  276. structure contains a doubly linked list entry, the address of a
  277. routine to call and a parameter to pass to that routine.
  278. Return Value:
  279. None
  280. --*/
  281. {
  282. KIRQL OldIrql;
  283. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  284. //
  285. // Insert the work item
  286. //
  287. KeAcquireSpinLock(&ACPIWorkerSpinLock, &OldIrql);
  288. if (IsListEmpty(&ACPIWorkQueue)) {
  289. KeSetEvent(&ACPIWorkToDoEvent, 0, FALSE);
  290. }
  291. InsertTailList(&ACPIWorkQueue, &WorkItem->List);
  292. KeReleaseSpinLock(&ACPIWorkerSpinLock, OldIrql);
  293. return;
  294. }