Source code of Windows XP (NT5)
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.

472 lines
15 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. apcsup.c
  5. Abstract:
  6. This module contains the support routines for the APC object. Functions
  7. are provided to insert in an APC queue and to deliver user and kernel
  8. mode APC's.
  9. Author:
  10. David N. Cutler (davec) 14-Mar-1989
  11. Environment:
  12. Kernel mode only.
  13. Revision History:
  14. --*/
  15. #include "ki.h"
  16. //
  17. // Define function prototypes for labels that delineate the bounds of the
  18. // pop SLIST code that is susceptable to causing corruption on suspend
  19. // operations.
  20. //
  21. VOID
  22. ExpInterlockedPopEntrySListEnd (
  23. VOID
  24. );
  25. VOID
  26. ExpInterlockedPopEntrySListResume (
  27. VOID
  28. );
  29. VOID
  30. KiDeliverApc (
  31. IN KPROCESSOR_MODE PreviousMode,
  32. IN PKEXCEPTION_FRAME ExceptionFrame,
  33. IN PKTRAP_FRAME TrapFrame
  34. )
  35. /*++
  36. Routine Description:
  37. This function is called from the APC interrupt code and when one or
  38. more of the APC pending flags are set at system exit and the previous
  39. IRQL is zero. All special kernel APC's are delivered first, followed
  40. by normal kernel APC's if one is not already in progress, and finally
  41. if the user APC queue is not empty, the user APC pending flag is set,
  42. and the previous mode is user, then a user APC is delivered. On entry
  43. to this routine IRQL is set to APC_LEVEL.
  44. N.B. The exception frame and trap frame addresses are only guaranteed
  45. to be valid if, and only if, the previous mode is user.
  46. Arguments:
  47. PreviousMode - Supplies the previous processor mode.
  48. ExceptionFrame - Supplies a pointer to an exception frame.
  49. TrapFrame - Supplies a pointer to a trap frame.
  50. Return Value:
  51. None.
  52. --*/
  53. {
  54. PKAPC Apc;
  55. PKKERNEL_ROUTINE KernelRoutine;
  56. KLOCK_QUEUE_HANDLE LockHandle;
  57. PLIST_ENTRY NextEntry;
  58. ULONG64 NewPC;
  59. PVOID NormalContext;
  60. PKNORMAL_ROUTINE NormalRoutine;
  61. ULONG64 PC;
  62. PKPROCESS Process;
  63. PVOID SystemArgument1;
  64. PVOID SystemArgument2;
  65. PKTHREAD Thread;
  66. PKTRAP_FRAME OldTrapFrame;
  67. //
  68. // If the thread was interrupted in the middle of the SLIST pop code,
  69. // then back up the PC to the start of the SLIST pop.
  70. //
  71. if (TrapFrame != NULL) {
  72. #if defined(_AMD64_)
  73. if ((TrapFrame->Rip >= (ULONG64)&ExpInterlockedPopEntrySListResume) &&
  74. (TrapFrame->Rip <= (ULONG64)&ExpInterlockedPopEntrySListEnd)) {
  75. TrapFrame->Rip = (ULONG64)&ExpInterlockedPopEntrySListResume;
  76. }
  77. #elif defined(_IA64_)
  78. //
  79. // Add the slot number so we do the right thing for the instruction
  80. // group containing the interlocked compare exchange.
  81. //
  82. PC = TrapFrame->StIIP + ((TrapFrame->StIPSR & IPSR_RI_MASK) >> PSR_RI);
  83. NewPC = (ULONG64)((PPLABEL_DESCRIPTOR)ExpInterlockedPopEntrySListResume)->EntryPoint;
  84. if ((PC >= NewPC) &&
  85. (PC <= (ULONG64)((PPLABEL_DESCRIPTOR)ExpInterlockedPopEntrySListEnd)->EntryPoint)) {
  86. TrapFrame->StIIP = NewPC;
  87. TrapFrame->StIPSR &= ~IPSR_RI_MASK;
  88. }
  89. #elif defined(_X86_)
  90. if ((TrapFrame->Eip >= (ULONG)&ExpInterlockedPopEntrySListResume) &&
  91. (TrapFrame->Eip <= (ULONG)&ExpInterlockedPopEntrySListEnd)) {
  92. TrapFrame->Eip = (ULONG)&ExpInterlockedPopEntrySListResume;
  93. }
  94. #else
  95. #error "No Target Architecture"
  96. #endif
  97. }
  98. //
  99. // Raise IRQL to dispatcher level and lock the APC queue.
  100. //
  101. Thread = KeGetCurrentThread();
  102. OldTrapFrame = Thread->TrapFrame;
  103. Thread->TrapFrame = TrapFrame;
  104. Process = Thread->ApcState.Process;
  105. KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);
  106. //
  107. // Get address of current thread object, clear kernel APC pending, and
  108. // check if any kernel mode APC's can be delivered.
  109. //
  110. Thread->ApcState.KernelApcPending = FALSE;
  111. while (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]) == FALSE) {
  112. NextEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
  113. Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
  114. KernelRoutine = Apc->KernelRoutine;
  115. NormalRoutine = Apc->NormalRoutine;
  116. NormalContext = Apc->NormalContext;
  117. SystemArgument1 = Apc->SystemArgument1;
  118. SystemArgument2 = Apc->SystemArgument2;
  119. if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {
  120. //
  121. // First entry in the kernel APC queue is a special kernel APC.
  122. // Remove the entry from the APC queue, set its inserted state
  123. // to FALSE, release dispatcher database lock, and call the kernel
  124. // routine. On return raise IRQL to dispatcher level and lock
  125. // dispatcher database lock.
  126. //
  127. RemoveEntryList(NextEntry);
  128. Apc->Inserted = FALSE;
  129. KeReleaseInStackQueuedSpinLock(&LockHandle);
  130. (KernelRoutine)(Apc,
  131. &NormalRoutine,
  132. &NormalContext,
  133. &SystemArgument1,
  134. &SystemArgument2);
  135. #if DBG
  136. if (KeGetCurrentIrql() != LockHandle.OldIrql) {
  137. KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
  138. KeGetCurrentIrql() << 16 | LockHandle.OldIrql << 8,
  139. (ULONG_PTR)KernelRoutine,
  140. (ULONG_PTR)Apc,
  141. (ULONG_PTR)NormalRoutine);
  142. }
  143. #endif
  144. KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);
  145. } else {
  146. //
  147. // First entry in the kernel APC queue is a normal kernel APC.
  148. // If there is not a normal kernel APC in progress and kernel
  149. // APC's are not disabled, then remove the entry from the APC
  150. // queue, set its inserted state to FALSE, release the APC queue
  151. // lock, call the specified kernel routine, set kernel APC in
  152. // progress, lower the IRQL to zero, and call the normal kernel
  153. // APC routine. On return raise IRQL to dispatcher level, lock
  154. // the APC queue, and clear kernel APC in progress.
  155. //
  156. if ((Thread->ApcState.KernelApcInProgress == FALSE) &&
  157. (Thread->KernelApcDisable == 0)) {
  158. RemoveEntryList(NextEntry);
  159. Apc->Inserted = FALSE;
  160. KeReleaseInStackQueuedSpinLock(&LockHandle);
  161. (KernelRoutine)(Apc,
  162. &NormalRoutine,
  163. &NormalContext,
  164. &SystemArgument1,
  165. &SystemArgument2);
  166. #if DBG
  167. if (KeGetCurrentIrql() != LockHandle.OldIrql) {
  168. KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
  169. KeGetCurrentIrql() << 16 | LockHandle.OldIrql << 8 | 1,
  170. (ULONG_PTR)KernelRoutine,
  171. (ULONG_PTR)Apc,
  172. (ULONG_PTR)NormalRoutine);
  173. }
  174. #endif
  175. if (NormalRoutine != (PKNORMAL_ROUTINE)NULL) {
  176. Thread->ApcState.KernelApcInProgress = TRUE;
  177. KeLowerIrql(0);
  178. (NormalRoutine)(NormalContext,
  179. SystemArgument1,
  180. SystemArgument2);
  181. KeRaiseIrql(APC_LEVEL, &LockHandle.OldIrql);
  182. }
  183. KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);
  184. Thread->ApcState.KernelApcInProgress = FALSE;
  185. } else {
  186. KeReleaseInStackQueuedSpinLock(&LockHandle);
  187. goto CheckProcess;
  188. }
  189. }
  190. }
  191. //
  192. // Kernel APC queue is empty. If the previous mode is user, user APC
  193. // pending is set, and the user APC queue is not empty, then remove
  194. // the first entry from the user APC queue, set its inserted state to
  195. // FALSE, clear user APC pending, release the dispatcher database lock,
  196. // and call the specified kernel routine. If the normal routine address
  197. // is not NULL on return from the kernel routine, then initialize the
  198. // user mode APC context and return. Otherwise, check to determine if
  199. // another user mode APC can be processed.
  200. //
  201. if ((IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]) == FALSE) &&
  202. (PreviousMode == UserMode) && (Thread->ApcState.UserApcPending != FALSE)) {
  203. Thread->ApcState.UserApcPending = FALSE;
  204. NextEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
  205. Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
  206. KernelRoutine = Apc->KernelRoutine;
  207. NormalRoutine = Apc->NormalRoutine;
  208. NormalContext = Apc->NormalContext;
  209. SystemArgument1 = Apc->SystemArgument1;
  210. SystemArgument2 = Apc->SystemArgument2;
  211. RemoveEntryList(NextEntry);
  212. Apc->Inserted = FALSE;
  213. KeReleaseInStackQueuedSpinLock(&LockHandle);
  214. (KernelRoutine)(Apc,
  215. &NormalRoutine,
  216. &NormalContext,
  217. &SystemArgument1,
  218. &SystemArgument2);
  219. if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {
  220. KeTestAlertThread(UserMode);
  221. } else {
  222. KiInitializeUserApc(ExceptionFrame,
  223. TrapFrame,
  224. NormalRoutine,
  225. NormalContext,
  226. SystemArgument1,
  227. SystemArgument2);
  228. }
  229. } else {
  230. KeReleaseInStackQueuedSpinLock(&LockHandle);
  231. }
  232. //
  233. // Check if process was attached during the APC routine.
  234. //
  235. CheckProcess:
  236. if (Thread->ApcState.Process != Process) {
  237. KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
  238. (ULONG_PTR)Process,
  239. (ULONG_PTR)Thread->ApcState.Process,
  240. (ULONG)Thread->ApcStateIndex,
  241. (ULONG)KeIsExecutingDpc());
  242. }
  243. Thread->TrapFrame = OldTrapFrame;
  244. return;
  245. }
  246. BOOLEAN
  247. FASTCALL
  248. KiInsertQueueApc (
  249. IN PKAPC Apc,
  250. IN KPRIORITY Increment
  251. )
  252. /*++
  253. Routine Description:
  254. This function inserts an APC object into a thread's APC queue. The address
  255. of the thread object, the APC queue, and the type of APC are all derived
  256. from the APC object. If the APC object is already in an APC queue, then
  257. no opertion is performed and a function value of FALSE is returned. Else
  258. the APC is inserted in the specified APC queue, its inserted state is set
  259. to TRUE, and a function value of TRUE is returned. The APC will actually
  260. be delivered when proper enabling conditions exist.
  261. N.B. The thread APC queue lock and the dispatcher database lock must both
  262. be held when this routine is called.
  263. Arguments:
  264. Apc - Supplies a pointer to a control object of type APC.
  265. Increment - Supplies the priority increment that is to be applied if
  266. queuing the APC causes a thread wait to be satisfied.
  267. Return Value:
  268. If the APC object is already in an APC queue, then a value of FALSE is
  269. returned. Else a value of TRUE is returned.
  270. --*/
  271. {
  272. KPROCESSOR_MODE ApcMode;
  273. PKAPC ApcEntry;
  274. PKAPC_STATE ApcState;
  275. BOOLEAN Inserted;
  276. PLIST_ENTRY ListEntry;
  277. PKTHREAD Thread;
  278. //
  279. // If the APC object is already in an APC queue, then set inserted to
  280. // FALSE. Else insert the APC object in the proper queue, set the APC
  281. // inserted state to TRUE, check to determine if the APC should be delivered
  282. // immediately, and set inserted to TRUE.
  283. //
  284. // For multiprocessor performance, the following code utilizes the fact
  285. // that kernel APC disable count is incremented before checking whether
  286. // the kernel APC queue is nonempty.
  287. //
  288. // See KeLeaveCriticalRegion().
  289. //
  290. Thread = Apc->Thread;
  291. if (Apc->Inserted) {
  292. Inserted = FALSE;
  293. } else {
  294. if (Apc->ApcStateIndex == InsertApcEnvironment) {
  295. Apc->ApcStateIndex = Thread->ApcStateIndex;
  296. }
  297. ApcState = Thread->ApcStatePointer[Apc->ApcStateIndex];
  298. //
  299. // Insert the APC after all other special APC entries selected by
  300. // the processor mode if the normal routine value is NULL. Else
  301. // insert the APC object at the tail of the APC queue selected by
  302. // the processor mode unless the APC mode is user and the address
  303. // of the special APC routine is exit thread, in which case insert
  304. // the APC at the front of the list and set user APC pending.
  305. //
  306. ApcMode = Apc->ApcMode;
  307. if (Apc->NormalRoutine != NULL) {
  308. if ((ApcMode != KernelMode) && (Apc->KernelRoutine == PsExitSpecialApc)) {
  309. Thread->ApcState.UserApcPending = TRUE;
  310. InsertHeadList(&ApcState->ApcListHead[ApcMode],
  311. &Apc->ApcListEntry);
  312. } else {
  313. InsertTailList(&ApcState->ApcListHead[ApcMode],
  314. &Apc->ApcListEntry);
  315. }
  316. } else {
  317. ListEntry = ApcState->ApcListHead[ApcMode].Blink;
  318. while (ListEntry != &ApcState->ApcListHead[ApcMode]) {
  319. ApcEntry = CONTAINING_RECORD(ListEntry, KAPC, ApcListEntry);
  320. if (ApcEntry->NormalRoutine == NULL) {
  321. break;
  322. }
  323. ListEntry = ListEntry->Blink;
  324. }
  325. InsertHeadList(ListEntry, &Apc->ApcListEntry);
  326. }
  327. Apc->Inserted = TRUE;
  328. //
  329. // If the APC index from the APC object matches the APC Index of
  330. // the thread, then check to determine if the APC should interrupt
  331. // thread execution or sequence the thread out of a wait state.
  332. //
  333. if (Apc->ApcStateIndex == Thread->ApcStateIndex) {
  334. //
  335. // If the processor mode of the APC is kernel, then check if
  336. // the APC should either interrupt the thread or sequence the
  337. // thread out of a Waiting state. Else check if the APC should
  338. // sequence the thread out of an alertable Waiting state.
  339. //
  340. if (ApcMode == KernelMode) {
  341. Thread->ApcState.KernelApcPending = TRUE;
  342. if (Thread->State == Running) {
  343. KiRequestApcInterrupt(Thread->NextProcessor);
  344. } else if ((Thread->State == Waiting) &&
  345. (Thread->WaitIrql == 0) &&
  346. ((Apc->NormalRoutine == NULL) ||
  347. ((Thread->KernelApcDisable == 0) &&
  348. (Thread->ApcState.KernelApcInProgress == FALSE)))) {
  349. KiUnwaitThread(Thread, STATUS_KERNEL_APC, Increment, NULL);
  350. }
  351. } else if ((Thread->State == Waiting) &&
  352. (Thread->WaitMode == UserMode) &&
  353. (Thread->Alertable || Thread->ApcState.UserApcPending)) {
  354. Thread->ApcState.UserApcPending = TRUE;
  355. KiUnwaitThread(Thread, STATUS_USER_APC, Increment, NULL);
  356. }
  357. }
  358. Inserted = TRUE;
  359. }
  360. //
  361. // Return whether the APC object was inserted in an APC queue.
  362. //
  363. return Inserted;
  364. }