dpc.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. /*++
  2. Copyright (c) 2013 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. dpc.c
  9. Abstract:
  10. This module implements support for Deferred Procedure Calls.
  11. Author:
  12. Evan Green 23-Jan-2013
  13. Environment:
  14. Kernel
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/kernel/kernel.h>
  20. #include "kep.h"
  21. //
  22. // ---------------------------------------------------------------- Definitions
  23. //
  24. #define DPC_ALLOCATION_TAG 0x21637044 // '!cpD'
  25. //
  26. // Define the default initial entropy mask.
  27. //
  28. #define DPC_ENTROPY_MASK_DEFAULT 0x0000001F
  29. //
  30. // ------------------------------------------------------ Data Type Definitions
  31. //
  32. //
  33. // ----------------------------------------------- Internal Function Prototypes
  34. //
  35. VOID
  36. KepQueueDpc (
  37. PDPC Dpc,
  38. PPROCESSOR_BLOCK Processor
  39. );
  40. //
  41. // -------------------------------------------------------------------- Globals
  42. //
  43. //
  44. // Define how often a DPC is timestamped to add entropy to the system. If all
  45. // the bits in the mask are zero of the processor's DPC count, then the DPC
  46. // is timestamped and entropy is added. This is a relatively heavy operation,
  47. // so it shouldn't occur too often.
  48. //
  49. UINTN KeDpcEntropyMask = DPC_ENTROPY_MASK_DEFAULT;
  50. //
  51. // ------------------------------------------------------------------ Functions
  52. //
  53. KERNEL_API
  54. PDPC
  55. KeCreateDpc (
  56. PDPC_ROUTINE DpcRoutine,
  57. PVOID UserData
  58. )
  59. /*++
  60. Routine Description:
  61. This routine creates a new DPC with the given routine and context data.
  62. Arguments:
  63. DpcRoutine - Supplies a pointer to the routine to call when the DPC fires.
  64. UserData - Supplies a context pointer that can be passed to the routine via
  65. the DPC when it is called.
  66. Return Value:
  67. Returns a pointer to the allocated and initialized (but not queued) DPC.
  68. --*/
  69. {
  70. PDPC Dpc;
  71. Dpc = MmAllocateNonPagedPool(sizeof(DPC), DPC_ALLOCATION_TAG);
  72. if (Dpc == NULL) {
  73. return NULL;
  74. }
  75. RtlZeroMemory(Dpc, sizeof(DPC));
  76. Dpc->DpcRoutine = DpcRoutine;
  77. Dpc->UserData = UserData;
  78. return Dpc;
  79. }
  80. KERNEL_API
  81. VOID
  82. KeDestroyDpc (
  83. PDPC Dpc
  84. )
  85. /*++
  86. Routine Description:
  87. This routine destroys a DPC. It will cancel the DPC if it is queued, and
  88. wait for it to finish if it is running. This routine must be called from
  89. low level.
  90. Arguments:
  91. Dpc - Supplies a pointer to the DPC to destroy.
  92. Return Value:
  93. None.
  94. --*/
  95. {
  96. ASSERT(KeGetRunLevel() == RunLevelLow);
  97. if (!KSUCCESS(KeCancelDpc(Dpc))) {
  98. KeFlushDpc(Dpc);
  99. }
  100. MmFreeNonPagedPool(Dpc);
  101. return;
  102. }
  103. KERNEL_API
  104. VOID
  105. KeQueueDpc (
  106. PDPC Dpc
  107. )
  108. /*++
  109. Routine Description:
  110. This routine queues a DPC on the current processor.
  111. Arguments:
  112. Dpc - Supplies a pointer to the DPC to queue.
  113. Return Value:
  114. None.
  115. --*/
  116. {
  117. KepQueueDpc(Dpc, NULL);
  118. return;
  119. }
  120. KERNEL_API
  121. VOID
  122. KeQueueDpcOnProcessor (
  123. PDPC Dpc,
  124. ULONG ProcessorNumber
  125. )
  126. /*++
  127. Routine Description:
  128. This routine queues a DPC on the given processor.
  129. Arguments:
  130. Dpc - Supplies a pointer to the DPC to queue.
  131. ProcessorNumber - Supplies the processor number of the processor to queue
  132. the DPC on.
  133. Return Value:
  134. None.
  135. --*/
  136. {
  137. ASSERT(ProcessorNumber < KeGetActiveProcessorCount());
  138. KepQueueDpc(Dpc, KeProcessorBlocks[ProcessorNumber]);
  139. return;
  140. }
  141. KERNEL_API
  142. KSTATUS
  143. KeCancelDpc (
  144. PDPC Dpc
  145. )
  146. /*++
  147. Routine Description:
  148. This routine attempts to cancel a DPC that has been queued.
  149. Arguments:
  150. Dpc - Supplies a pointer to the DPC to cancel.
  151. Return Value:
  152. STATUS_SUCCESS if the DPC was successfully pulled out of a queue.
  153. STATUS_TOO_LATE if the DPC has already started running.
  154. --*/
  155. {
  156. BOOL Enabled;
  157. ULONG Processor;
  158. PPROCESSOR_BLOCK ProcessorBlock;
  159. ULONG ProcessorCount;
  160. KSTATUS Status;
  161. ProcessorCount = KeGetActiveProcessorCount();
  162. Status = STATUS_TOO_LATE;
  163. //
  164. // Attempt to chase the DPC around whichever processor it's running on as
  165. // long as it's queued on a processor's list.
  166. //
  167. while ((Dpc->UseCount != 0) &&
  168. ((Dpc->Flags & DPC_FLAG_QUEUED_ON_PROCESSOR) != 0)) {
  169. Processor = Dpc->Processor;
  170. if (Processor >= ProcessorCount) {
  171. KeCrashSystem(CRASH_DPC_FAILURE,
  172. DpcCrashReasonCorrupt,
  173. (UINTN)Dpc,
  174. Processor,
  175. ProcessorCount);
  176. }
  177. //
  178. // Grab the DPC lock for the processor the DPC is on. If the DPC is
  179. // still active for that same processor and is still on the queue, pull
  180. // it off the queue. It may have been pulled off the processor's DPC
  181. // list and be on a local list for execution. If that's the case, then
  182. // it is too late to cancel the DPC.
  183. //
  184. ProcessorBlock = KeProcessorBlocks[Processor];
  185. Enabled = ArDisableInterrupts();
  186. KeAcquireSpinLock(&(ProcessorBlock->DpcLock));
  187. if ((Dpc->UseCount != 0) &&
  188. (Dpc->Processor == Processor) &&
  189. ((Dpc->Flags & DPC_FLAG_QUEUED_ON_PROCESSOR) != 0)) {
  190. LIST_REMOVE(&(Dpc->ListEntry));
  191. Dpc->Flags &= ~DPC_FLAG_QUEUED_ON_PROCESSOR;
  192. Dpc->ListEntry.Next = NULL;
  193. Status = STATUS_SUCCESS;
  194. }
  195. KeReleaseSpinLock(&(ProcessorBlock->DpcLock));
  196. if (Enabled != FALSE) {
  197. ArEnableInterrupts();
  198. }
  199. //
  200. // If the DPC was successfully pulled off the queue, decrement its use
  201. // count and return successfully.
  202. //
  203. if (KSUCCESS(Status)) {
  204. RtlAtomicAdd32(&(Dpc->UseCount), -1);
  205. break;
  206. }
  207. }
  208. return Status;
  209. }
  210. KERNEL_API
  211. VOID
  212. KeFlushDpc (
  213. PDPC Dpc
  214. )
  215. /*++
  216. Routine Description:
  217. This routine does not return until the given DPC is out of the system. This
  218. means that the DPC is neither queued nor running. It's worth noting that
  219. this routine busy spins at dispatch level, and should therefore be used
  220. only sparingly. This routine can only be called from low level.
  221. Arguments:
  222. Dpc - Supplies a pointer to the DPC to wait for.
  223. Return Value:
  224. None.
  225. --*/
  226. {
  227. //
  228. // If the runlevel were dispatch or higher and the DPC was queued on this
  229. // processor, it would never run. It's OK if the runlevel is dispatch and
  230. // the DPC is queued on another processor.
  231. //
  232. ASSERT((KeGetRunLevel() == RunLevelLow) ||
  233. ((KeGetRunLevel() == RunLevelDispatch) &&
  234. (Dpc->Processor != KeGetCurrentProcessorNumber())));
  235. while (Dpc->UseCount != 0) {
  236. ArProcessorYield();
  237. }
  238. return;
  239. }
  240. VOID
  241. KepExecutePendingDpcs (
  242. VOID
  243. )
  244. /*++
  245. Routine Description:
  246. This routine executes any pending DPCs on the current processor. This
  247. routine should only be executed internally by the scheduler. It must be
  248. called at dispatch level. Interrupts must be disabled upon entry, but will
  249. be enabled on exit.
  250. Arguments:
  251. None.
  252. Return Value:
  253. None.
  254. --*/
  255. {
  256. PLIST_ENTRY CurrentEntry;
  257. PDPC Dpc;
  258. LIST_ENTRY LocalList;
  259. CYCLE_ACCOUNT PreviousPeriod;
  260. PPROCESSOR_BLOCK ProcessorBlock;
  261. ASSERT(KeGetRunLevel() == RunLevelDispatch);
  262. ProcessorBlock = KeGetCurrentProcessorBlock();
  263. //
  264. // Return immediately if the list is empty.
  265. //
  266. if (LIST_EMPTY(&(ProcessorBlock->DpcList)) != FALSE) {
  267. ArEnableInterrupts();
  268. return;
  269. }
  270. PreviousPeriod = KeBeginCycleAccounting(CycleAccountInterrupt);
  271. INITIALIZE_LIST_HEAD(&LocalList);
  272. //
  273. // Acquire the lock long enough to move the list off of the processor block
  274. // list and mark that each entry is no longer queued on said list.
  275. //
  276. KeAcquireSpinLock(&(ProcessorBlock->DpcLock));
  277. if (LIST_EMPTY(&(ProcessorBlock->DpcList)) == FALSE) {
  278. MOVE_LIST(&(ProcessorBlock->DpcList), &LocalList);
  279. INITIALIZE_LIST_HEAD(&(ProcessorBlock->DpcList));
  280. CurrentEntry = LocalList.Next;
  281. while (CurrentEntry != &LocalList) {
  282. Dpc = LIST_VALUE(CurrentEntry, DPC, ListEntry);
  283. Dpc->Flags &= ~DPC_FLAG_QUEUED_ON_PROCESSOR;
  284. CurrentEntry = CurrentEntry->Next;
  285. }
  286. }
  287. KeReleaseSpinLock(&(ProcessorBlock->DpcLock));
  288. ArEnableInterrupts();
  289. //
  290. // Set the clock to periodic mode before executing the DPCs. A DPC may
  291. // depend on the clock making forward progress (e.g. a timeout may be
  292. // implemented using recent snaps of the time counter rather than querying
  293. // the hardware directly).
  294. //
  295. if (LIST_EMPTY(&LocalList) == FALSE) {
  296. KepSetClockToPeriodic(ProcessorBlock);
  297. }
  298. //
  299. // Now execute all pending DPCs.
  300. //
  301. ASSERT(ProcessorBlock->DpcInProgress == NULL);
  302. while (LIST_EMPTY(&LocalList) == FALSE) {
  303. CurrentEntry = LocalList.Next;
  304. Dpc = LIST_VALUE(CurrentEntry, DPC, ListEntry);
  305. ProcessorBlock->DpcInProgress = Dpc;
  306. //
  307. // Pull the DPC off the local list and set it's next pointer to NULL to
  308. // indicate that it is not queued.
  309. //
  310. LIST_REMOVE(CurrentEntry);
  311. Dpc->ListEntry.Next = NULL;
  312. //
  313. // Call the DPC routine.
  314. //
  315. Dpc->DpcRoutine(Dpc);
  316. //
  317. // Decrement the use count to indicate that the system (or at least
  318. // this processor on this iteration) is done looking at this thing.
  319. //
  320. RtlAtomicAdd32(&(Dpc->UseCount), -1);
  321. //
  322. // Add one to the DPC counter, and potentially add entropy.
  323. //
  324. ProcessorBlock->DpcCount += 1;
  325. if ((ProcessorBlock->DpcCount & KeDpcEntropyMask) == 0) {
  326. KepAddTimePointEntropy();
  327. }
  328. }
  329. ProcessorBlock->DpcInProgress = NULL;
  330. KeBeginCycleAccounting(PreviousPeriod);
  331. return;
  332. }
  333. //
  334. // --------------------------------------------------------- Internal Functions
  335. //
  336. VOID
  337. KepQueueDpc (
  338. PDPC Dpc,
  339. PPROCESSOR_BLOCK Processor
  340. )
  341. /*++
  342. Routine Description:
  343. This routine queues a DPC on the given processor. If the DPC is being
  344. queued on the current processor, and the current runlevel is less than or
  345. equal to dispatch, then the DPC routine is run immediately. This is useful
  346. for things like timer expiration.
  347. Arguments:
  348. Dpc - Supplies a pointer to the DPC to queue.
  349. Processor - Supplies a pointer to the processor block.
  350. Return Value:
  351. None.
  352. --*/
  353. {
  354. PPROCESSOR_BLOCK CurrentProcessor;
  355. BOOL Enabled;
  356. RUNLEVEL OldRunLevel;
  357. Enabled = ArDisableInterrupts();
  358. CurrentProcessor = KeGetCurrentProcessorBlock();
  359. if (Processor == NULL) {
  360. Processor = CurrentProcessor;
  361. }
  362. if (Dpc->ListEntry.Next != NULL) {
  363. KeCrashSystem(CRASH_DPC_FAILURE,
  364. DpcCrashReasonDoubleQueueDpc,
  365. (UINTN)Dpc,
  366. 0,
  367. 0);
  368. }
  369. ASSERT((Dpc->Flags & DPC_FLAG_QUEUED_ON_PROCESSOR) == 0);
  370. if (Dpc->DpcRoutine == NULL) {
  371. KeCrashSystem(CRASH_DPC_FAILURE,
  372. DpcCrashReasonNullRoutine,
  373. (UINTN)Dpc,
  374. 0,
  375. 0);
  376. }
  377. //
  378. // Run the DPC directly if it's on the current processor and the runlevel
  379. // is at or below dispatch.
  380. //
  381. if ((Processor == CurrentProcessor) &&
  382. (Processor->RunLevel <= RunLevelDispatch) &&
  383. (Enabled != FALSE)) {
  384. RtlAtomicAdd32(&(Dpc->UseCount), 1);
  385. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  386. if (Enabled != FALSE) {
  387. ArEnableInterrupts();
  388. }
  389. Dpc->DpcRoutine(Dpc);
  390. KeLowerRunLevel(OldRunLevel);
  391. RtlAtomicAdd32(&(Dpc->UseCount), -1);
  392. //
  393. // Really queue the DPC on the destination processor.
  394. //
  395. } else {
  396. RtlAtomicAdd32(&(Dpc->UseCount), 1);
  397. Dpc->Processor = Processor->ProcessorNumber;
  398. KeAcquireSpinLock(&(Processor->DpcLock));
  399. INSERT_BEFORE(&(Dpc->ListEntry), &(Processor->DpcList));
  400. Dpc->Flags |= DPC_FLAG_QUEUED_ON_PROCESSOR;
  401. KeReleaseSpinLock(&(Processor->DpcLock));
  402. Processor->PendingDispatchInterrupt = TRUE;
  403. //
  404. // Raise to dispatch before enabling interrupts to ensure a processor
  405. // switch doesn't happen before poking the clock.
  406. //
  407. OldRunLevel = RunLevelCount;
  408. if (CurrentProcessor->RunLevel < RunLevelDispatch) {
  409. OldRunLevel = KeRaiseRunLevel(RunLevelDispatch);
  410. }
  411. if (Enabled != FALSE) {
  412. ArEnableInterrupts();
  413. }
  414. //
  415. // Ensure the processor is awake to go handle a DPC.
  416. //
  417. if (Processor != CurrentProcessor) {
  418. KepSetClockToPeriodic(Processor);
  419. }
  420. if (OldRunLevel != RunLevelCount) {
  421. KeLowerRunLevel(OldRunLevel);
  422. }
  423. }
  424. return;
  425. }