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.

606 lines
20 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. KiCheckForKernelApcDelivery (
  31. VOID
  32. )
  33. /*++
  34. Routine Description:
  35. This function checks to detemine if a kernel APC can be delivered
  36. immediately to the current thread or a kernel APC interrupt should
  37. be requested. On entry to this routine the following conditions are
  38. true:
  39. 1. Special kernel APCs are enabled for the current thread.
  40. 2. Normal kernel APCs may also be enabled for the current thread.
  41. 3. The kernel APC queue is not empty.
  42. N.B. This routine is only called by kernel code that leaves a guarded
  43. or critcial region.
  44. Arguments:
  45. None.
  46. Return Value:
  47. None.
  48. --*/
  49. {
  50. //
  51. // If the current IRQL is passive level, then a kernel APC can be
  52. // delivered immediately. Otherwise, an APC interrupt must be
  53. // requested.
  54. //
  55. if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
  56. KfRaiseIrql(APC_LEVEL);
  57. KiDeliverApc(KernelMode, NULL, NULL);
  58. KeLowerIrql(PASSIVE_LEVEL);
  59. } else {
  60. KeGetCurrentThread()->ApcState.KernelApcPending = TRUE;
  61. KiRequestSoftwareInterrupt(APC_LEVEL);
  62. }
  63. return;
  64. }
  65. VOID
  66. KiDeliverApc (
  67. IN KPROCESSOR_MODE PreviousMode,
  68. IN PKEXCEPTION_FRAME ExceptionFrame,
  69. IN PKTRAP_FRAME TrapFrame
  70. )
  71. /*++
  72. Routine Description:
  73. This function is called from the APC interrupt code and when one or
  74. more of the APC pending flags are set at system exit and the previous
  75. IRQL is zero. All special kernel APC's are delivered first, followed
  76. by normal kernel APC's if one is not already in progress, and finally
  77. if the user APC queue is not empty, the user APC pending flag is set,
  78. and the previous mode is user, then a user APC is delivered. On entry
  79. to this routine IRQL is set to APC_LEVEL.
  80. N.B. The exception frame and trap frame addresses are only guaranteed
  81. to be valid if, and only if, the previous mode is user.
  82. Arguments:
  83. PreviousMode - Supplies the previous processor mode.
  84. ExceptionFrame - Supplies a pointer to an exception frame.
  85. TrapFrame - Supplies a pointer to a trap frame.
  86. Return Value:
  87. None.
  88. --*/
  89. {
  90. PKAPC Apc;
  91. PKKERNEL_ROUTINE KernelRoutine;
  92. KLOCK_QUEUE_HANDLE LockHandle;
  93. PLIST_ENTRY NextEntry;
  94. PVOID NormalContext;
  95. PKNORMAL_ROUTINE NormalRoutine;
  96. PKTRAP_FRAME OldTrapFrame;
  97. PKPROCESS Process;
  98. PVOID SystemArgument1;
  99. PVOID SystemArgument2;
  100. PKTHREAD Thread;
  101. //
  102. // If the thread was interrupted in the middle of the SLIST pop code,
  103. // then back up the PC to the start of the SLIST pop.
  104. //
  105. if (TrapFrame != NULL) {
  106. #if defined(_AMD64_)
  107. if ((TrapFrame->Rip >= (ULONG64)&ExpInterlockedPopEntrySListResume) &&
  108. (TrapFrame->Rip <= (ULONG64)&ExpInterlockedPopEntrySListEnd)) {
  109. TrapFrame->Rip = (ULONG64)&ExpInterlockedPopEntrySListResume;
  110. }
  111. #elif defined(_IA64_)
  112. ULONG64 PC;
  113. ULONG64 NewPC;
  114. //
  115. // Add the slot number so we do the right thing for the instruction
  116. // group containing the interlocked compare exchange.
  117. //
  118. PC = TrapFrame->StIIP + ((TrapFrame->StIPSR & IPSR_RI_MASK) >> PSR_RI);
  119. NewPC = (ULONG64)((PPLABEL_DESCRIPTOR)(ULONG_PTR)ExpInterlockedPopEntrySListResume)->EntryPoint;
  120. if ((PC >= NewPC) &&
  121. (PC <= (ULONG64)((PPLABEL_DESCRIPTOR)(ULONG_PTR)ExpInterlockedPopEntrySListEnd)->EntryPoint)) {
  122. TrapFrame->StIIP = NewPC;
  123. TrapFrame->StIPSR &= ~IPSR_RI_MASK;
  124. }
  125. #elif defined(_X86_)
  126. if ((TrapFrame->Eip >= (ULONG)&ExpInterlockedPopEntrySListResume) &&
  127. (TrapFrame->Eip <= (ULONG)&ExpInterlockedPopEntrySListEnd)) {
  128. TrapFrame->Eip = (ULONG)&ExpInterlockedPopEntrySListResume;
  129. }
  130. #else
  131. #error "No Target Architecture"
  132. #endif
  133. }
  134. //
  135. // Save the current thread trap frame address and set the thread trap
  136. // frame address to the new trap frame. This will prevent a user mode
  137. // exception from being raised within an APC routine.
  138. //
  139. Thread = KeGetCurrentThread();
  140. OldTrapFrame = Thread->TrapFrame;
  141. Thread->TrapFrame = TrapFrame;
  142. //
  143. // If special APC are not disabled, then attempt to deliver one or more
  144. // APCs.
  145. //
  146. Process = Thread->ApcState.Process;
  147. Thread->ApcState.KernelApcPending = FALSE;
  148. if (Thread->SpecialApcDisable == 0) {
  149. //
  150. // If the kernel APC queue is not empty, then attempt to deliver a
  151. // kernel APC.
  152. //
  153. // N.B. The following test is not synchronized with the APC insertion
  154. // code. However, when an APC is inserted in the kernel queue of
  155. // a running thread an APC interrupt is requested. Therefore, if
  156. // the following test were to falsely return that the kernel APC
  157. // queue was empty, an APC interrupt would immediately cause this
  158. // code to be executed a second time in which case the kernel APC
  159. // queue would found to contain an entry.
  160. //
  161. KeMemoryBarrier();
  162. while (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]) == FALSE) {
  163. //
  164. // Raise IRQL to dispatcher level, lock the APC queue, and check
  165. // if any kernel mode APC's can be delivered.
  166. //
  167. KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);
  168. //
  169. // If the kernel APC queue is now empty because of the removal of
  170. // one or more entries, then release the APC lock, and attempt to
  171. // deliver a user APC.
  172. //
  173. NextEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
  174. if (NextEntry == &Thread->ApcState.ApcListHead[KernelMode]) {
  175. KeReleaseInStackQueuedSpinLock(&LockHandle);
  176. break;
  177. }
  178. //
  179. // Get the address of the APC object and determine the type of
  180. // APC.
  181. //
  182. Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
  183. KernelRoutine = Apc->KernelRoutine;
  184. NormalRoutine = Apc->NormalRoutine;
  185. NormalContext = Apc->NormalContext;
  186. SystemArgument1 = Apc->SystemArgument1;
  187. SystemArgument2 = Apc->SystemArgument2;
  188. if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {
  189. //
  190. // First entry in the kernel APC queue is a special kernel APC.
  191. // Remove the entry from the APC queue, set its inserted state
  192. // to FALSE, release dispatcher database lock, and call the kernel
  193. // routine. On return raise IRQL to dispatcher level and lock
  194. // dispatcher database lock.
  195. //
  196. RemoveEntryList(NextEntry);
  197. Apc->Inserted = FALSE;
  198. KeReleaseInStackQueuedSpinLock(&LockHandle);
  199. (KernelRoutine)(Apc,
  200. &NormalRoutine,
  201. &NormalContext,
  202. &SystemArgument1,
  203. &SystemArgument2);
  204. #if DBG
  205. if (KeGetCurrentIrql() != LockHandle.OldIrql) {
  206. KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
  207. KeGetCurrentIrql() << 16 | LockHandle.OldIrql << 8,
  208. (ULONG_PTR)KernelRoutine,
  209. (ULONG_PTR)Apc,
  210. (ULONG_PTR)NormalRoutine);
  211. }
  212. #endif
  213. } else {
  214. //
  215. // First entry in the kernel APC queue is a normal kernel APC.
  216. // If there is not a normal kernel APC in progress and kernel
  217. // APC's are not disabled, then remove the entry from the APC
  218. // queue, set its inserted state to FALSE, release the APC queue
  219. // lock, call the specified kernel routine, set kernel APC in
  220. // progress, lower the IRQL to zero, and call the normal kernel
  221. // APC routine. On return raise IRQL to dispatcher level, lock
  222. // the APC queue, and clear kernel APC in progress.
  223. //
  224. if ((Thread->ApcState.KernelApcInProgress == FALSE) &&
  225. (Thread->KernelApcDisable == 0)) {
  226. RemoveEntryList(NextEntry);
  227. Apc->Inserted = FALSE;
  228. KeReleaseInStackQueuedSpinLock(&LockHandle);
  229. (KernelRoutine)(Apc,
  230. &NormalRoutine,
  231. &NormalContext,
  232. &SystemArgument1,
  233. &SystemArgument2);
  234. #if DBG
  235. if (KeGetCurrentIrql() != LockHandle.OldIrql) {
  236. KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
  237. KeGetCurrentIrql() << 16 | LockHandle.OldIrql << 8 | 1,
  238. (ULONG_PTR)KernelRoutine,
  239. (ULONG_PTR)Apc,
  240. (ULONG_PTR)NormalRoutine);
  241. }
  242. #endif
  243. if (NormalRoutine != (PKNORMAL_ROUTINE)NULL) {
  244. Thread->ApcState.KernelApcInProgress = TRUE;
  245. KeLowerIrql(0);
  246. (NormalRoutine)(NormalContext,
  247. SystemArgument1,
  248. SystemArgument2);
  249. KeRaiseIrql(APC_LEVEL, &LockHandle.OldIrql);
  250. }
  251. Thread->ApcState.KernelApcInProgress = FALSE;
  252. } else {
  253. KeReleaseInStackQueuedSpinLock(&LockHandle);
  254. goto CheckProcess;
  255. }
  256. }
  257. }
  258. //
  259. // Kernel APC queue is empty. If the previous mode is user, user APC
  260. // pending is set, and the user APC queue is not empty, then remove
  261. // the first entry from the user APC queue, set its inserted state to
  262. // FALSE, clear user APC pending, release the dispatcher database lock,
  263. // and call the specified kernel routine. If the normal routine address
  264. // is not NULL on return from the kernel routine, then initialize the
  265. // user mode APC context and return. Otherwise, check to determine if
  266. // another user mode APC can be processed.
  267. //
  268. // N.B. There is no race condition associated with checking the APC
  269. // queue outside the APC lock. User APCs are always delivered at
  270. // system exit and never interrupt the execution of the thread
  271. // in the kernel.
  272. //
  273. if ((IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]) == FALSE) &&
  274. (PreviousMode == UserMode) &&
  275. (Thread->ApcState.UserApcPending != FALSE)) {
  276. //
  277. // Raise IRQL to dispatcher level, lock the APC queue, and deliver
  278. // a user mode APC.
  279. //
  280. KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);
  281. //
  282. // If the user APC queue is now empty because of the removal of
  283. // one or more entries, then release the APC lock and exit.
  284. //
  285. Thread->ApcState.UserApcPending = FALSE;
  286. NextEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
  287. if (NextEntry == &Thread->ApcState.ApcListHead[UserMode]) {
  288. KeReleaseInStackQueuedSpinLock(&LockHandle);
  289. goto CheckProcess;
  290. }
  291. Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
  292. KernelRoutine = Apc->KernelRoutine;
  293. NormalRoutine = Apc->NormalRoutine;
  294. NormalContext = Apc->NormalContext;
  295. SystemArgument1 = Apc->SystemArgument1;
  296. SystemArgument2 = Apc->SystemArgument2;
  297. RemoveEntryList(NextEntry);
  298. Apc->Inserted = FALSE;
  299. KeReleaseInStackQueuedSpinLock(&LockHandle);
  300. (KernelRoutine)(Apc,
  301. &NormalRoutine,
  302. &NormalContext,
  303. &SystemArgument1,
  304. &SystemArgument2);
  305. if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {
  306. KeTestAlertThread(UserMode);
  307. } else {
  308. KiInitializeUserApc(ExceptionFrame,
  309. TrapFrame,
  310. NormalRoutine,
  311. NormalContext,
  312. SystemArgument1,
  313. SystemArgument2);
  314. }
  315. }
  316. }
  317. //
  318. // Check if process was attached during the APC routine.
  319. //
  320. CheckProcess:
  321. if (Thread->ApcState.Process != Process) {
  322. KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
  323. (ULONG_PTR)Process,
  324. (ULONG_PTR)Thread->ApcState.Process,
  325. (ULONG)Thread->ApcStateIndex,
  326. (ULONG)KeIsExecutingDpc());
  327. }
  328. //
  329. // Restore the previous thread trap frame address.
  330. //
  331. Thread->TrapFrame = OldTrapFrame;
  332. return;
  333. }
  334. VOID
  335. FASTCALL
  336. KiInsertQueueApc (
  337. IN PKAPC InApc,
  338. IN KPRIORITY Increment
  339. )
  340. /*++
  341. Routine Description:
  342. This function inserts an APC object into a thread's APC queue. The address
  343. of the thread object, the APC queue, and the type of APC are all derived
  344. from the APC object. If the APC object is already in an APC queue, then
  345. no opertion is performed and a function value of FALSE is returned. Else
  346. the APC is inserted in the specified APC queue, its inserted state is set
  347. to TRUE, and a function value of TRUE is returned. The APC will actually
  348. be delivered when proper enabling conditions exist.
  349. N.B. The thread APC queue lock must be held when this routine is called.
  350. N.B. It is the responsibility of the caller to ensure that the APC is not
  351. already inserted in an APC queue and to set the Inserted field of
  352. the APC.
  353. Arguments:
  354. InApc - Supplies a pointer to a control object of type APC.
  355. Increment - Supplies the priority increment that is to be applied if
  356. queuing the APC causes a thread wait to be satisfied.
  357. Return Value:
  358. None.
  359. --*/
  360. {
  361. KPROCESSOR_MODE ApcMode;
  362. PKAPC ApcEntry;
  363. PKAPC_STATE ApcState;
  364. PLIST_ENTRY ListEntry;
  365. PKTHREAD Thread;
  366. KTHREAD_STATE ThreadState;
  367. PKAPC Apc = InApc;
  368. //
  369. // Insert the APC object in the specified APC queue, set the APC inserted
  370. // state to TRUE, and check to determine if the APC should be delivered
  371. // immediately.
  372. //
  373. // For multiprocessor performance, the following code utilizes the fact
  374. // that kernel APC disable count is incremented before checking whether
  375. // the kernel APC queue is nonempty.
  376. //
  377. // See KeLeaveCriticalRegion().
  378. //
  379. Thread = Apc->Thread;
  380. if (Apc->ApcStateIndex == InsertApcEnvironment) {
  381. Apc->ApcStateIndex = Thread->ApcStateIndex;
  382. }
  383. ApcState = Thread->ApcStatePointer[Apc->ApcStateIndex];
  384. //
  385. // Insert the APC after all other special APC entries selected by
  386. // the processor mode if the normal routine value is NULL. Else
  387. // insert the APC object at the tail of the APC queue selected by
  388. // the processor mode unless the APC mode is user and the address
  389. // of the special APC routine is exit thread, in which case insert
  390. // the APC at the front of the list and set user APC pending.
  391. //
  392. ApcMode = Apc->ApcMode;
  393. ASSERT (Apc->Inserted == TRUE);
  394. if (Apc->NormalRoutine != NULL) {
  395. if ((ApcMode != KernelMode) && (Apc->KernelRoutine == PsExitSpecialApc)) {
  396. Thread->ApcState.UserApcPending = TRUE;
  397. InsertHeadList(&ApcState->ApcListHead[ApcMode],
  398. &Apc->ApcListEntry);
  399. } else {
  400. InsertTailList(&ApcState->ApcListHead[ApcMode],
  401. &Apc->ApcListEntry);
  402. }
  403. } else {
  404. ListEntry = ApcState->ApcListHead[ApcMode].Blink;
  405. while (ListEntry != &ApcState->ApcListHead[ApcMode]) {
  406. ApcEntry = CONTAINING_RECORD(ListEntry, KAPC, ApcListEntry);
  407. if (ApcEntry->NormalRoutine == NULL) {
  408. break;
  409. }
  410. ListEntry = ListEntry->Blink;
  411. }
  412. InsertHeadList(ListEntry, &Apc->ApcListEntry);
  413. }
  414. //
  415. // If the APC index from the APC object matches the APC Index of
  416. // the thread, then check to determine if the APC should interrupt
  417. // thread execution or sequence the thread out of a wait state.
  418. //
  419. if (Apc->ApcStateIndex == Thread->ApcStateIndex) {
  420. //
  421. // Lock the dispacher database and test the processor mode.
  422. //
  423. // If the processor mode of the APC is kernel, then check if
  424. // the APC should either interrupt the thread or sequence the
  425. // thread out of a Waiting state. Else check if the APC should
  426. // sequence the thread out of an alertable Waiting state.
  427. //
  428. KiLockDispatcherDatabaseAtSynchLevel();
  429. if (ApcMode == KernelMode) {
  430. //
  431. // Thread transitions from the standby state to the running
  432. // state can occur from the idle thread without holding the
  433. // dispatcher lock. Reading the thread state after setting
  434. // the kernel APC pending flag prevents the code from not
  435. // delivering the APC interrupt in this case.
  436. //
  437. ASSERT((Thread != KeGetCurrentThread()) || (Thread->State == Running));
  438. KeMemoryBarrier();
  439. Thread->ApcState.KernelApcPending = TRUE;
  440. KeMemoryBarrier();
  441. ThreadState = Thread->State;
  442. if (ThreadState == Running) {
  443. KiRequestApcInterrupt(Thread->NextProcessor);
  444. } else if ((ThreadState == Waiting) &&
  445. (Thread->WaitIrql == 0) &&
  446. (Thread->SpecialApcDisable == 0) &&
  447. ((Apc->NormalRoutine == NULL) ||
  448. ((Thread->KernelApcDisable == 0) &&
  449. (Thread->ApcState.KernelApcInProgress == FALSE)))) {
  450. KiUnwaitThread(Thread, STATUS_KERNEL_APC, Increment);
  451. }
  452. } else if ((Thread->State == Waiting) &&
  453. (Thread->WaitMode == UserMode) &&
  454. (Thread->Alertable || Thread->ApcState.UserApcPending)) {
  455. Thread->ApcState.UserApcPending = TRUE;
  456. KiUnwaitThread(Thread, STATUS_USER_APC, Increment);
  457. }
  458. //
  459. // Unlock the dispatcher database.
  460. //
  461. KiUnlockDispatcherDatabaseFromSynchLevel();
  462. }
  463. return;
  464. }