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.

494 lines
10 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. dpcobj.c
  5. Abstract:
  6. This module implements the kernel DPC object. Functions are provided
  7. to initialize, insert, and remove DPC objects.
  8. Author:
  9. David N. Cutler (davec) 6-Mar-1989
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. --*/
  14. #include "ki.h"
  15. #ifdef ALLOC_PRAGMA
  16. #pragma alloc_text(PAGE, KeFlushQueuedDpcs)
  17. #endif
  18. //
  19. // The following assert macro is used to check that an input dpc is
  20. // really a kdpc and not something else, like deallocated pool.
  21. //
  22. #define ASSERT_DPC(E) { \
  23. ASSERT((E)->Type == DpcObject); \
  24. }
  25. VOID
  26. KeInitializeDpc (
  27. IN PRKDPC Dpc,
  28. IN PKDEFERRED_ROUTINE DeferredRoutine,
  29. IN PVOID DeferredContext
  30. )
  31. /*++
  32. Routine Description:
  33. This function initializes a kernel DPC object. The deferred routine
  34. and context parameter are stored in the DPC object.
  35. Arguments:
  36. Dpc - Supplies a pointer to a control object of type DPC.
  37. DeferredRoutine - Supplies a pointer to a function that is called when
  38. the DPC object is removed from the current processor's DPC queue.
  39. DeferredContext - Supplies a pointer to an arbitrary data structure which is
  40. to be passed to the function specified by the DeferredRoutine parameter.
  41. Return Value:
  42. None.
  43. --*/
  44. {
  45. //
  46. // Initialize standard control object header.
  47. //
  48. Dpc->Type = DpcObject;
  49. Dpc->Number = 0;
  50. Dpc->Importance = MediumImportance;
  51. //
  52. // Initialize deferred routine address and deferred context parameter.
  53. //
  54. Dpc->DeferredRoutine = DeferredRoutine;
  55. Dpc->DeferredContext = DeferredContext;
  56. Dpc->Lock = NULL;
  57. return;
  58. }
  59. BOOLEAN
  60. KeInsertQueueDpc (
  61. IN PRKDPC Dpc,
  62. IN PVOID SystemArgument1,
  63. IN PVOID SystemArgument2
  64. )
  65. /*++
  66. Routine Description:
  67. This function inserts a DPC object into the DPC queue. If the DPC object
  68. is already in the DPC queue, then no operation is performed. Otherwise,
  69. the DPC object is inserted in the DPC queue and a dispatch interrupt is
  70. requested.
  71. Arguments:
  72. Dpc - Supplies a pointer to a control object of type DPC.
  73. SystemArgument1, SystemArgument2 - Supply a set of two arguments that
  74. contain untyped data provided by the executive.
  75. Return Value:
  76. If the DPC object is already in a DPC queue, then a value of FALSE is
  77. returned. Otherwise a value of TRUE is returned.
  78. --*/
  79. {
  80. ULONG Index;
  81. PKSPIN_LOCK Lock;
  82. KIRQL OldIrql;
  83. PKPRCB Prcb;
  84. ULONG Processor;
  85. ASSERT_DPC(Dpc);
  86. //
  87. // Disable interrupts.
  88. //
  89. KeRaiseIrql(HIGH_LEVEL, &OldIrql);
  90. //
  91. // Acquire the DPC queue lock for the specified target processor.
  92. //
  93. #if !defined(NT_UP)
  94. if (Dpc->Number >= MAXIMUM_PROCESSORS) {
  95. Processor = Dpc->Number - MAXIMUM_PROCESSORS;
  96. Prcb = KiProcessorBlock[Processor];
  97. } else {
  98. Prcb = KeGetCurrentPrcb();
  99. }
  100. KiAcquireSpinLock(&Prcb->DpcLock);
  101. #else
  102. Prcb = KeGetCurrentPrcb();
  103. #endif
  104. //
  105. // If the DPC object is not in a DPC queue, then store the system
  106. // arguments, insert the DPC object in the DPC queue, increment the
  107. // number of DPCs queued to the target processor, increment the DPC
  108. // queue depth, set the address of the DPC target DPC spinlock, and
  109. // request a dispatch interrupt if appropriate.
  110. //
  111. if ((Lock = InterlockedCompareExchangePointer(&Dpc->Lock, &Prcb->DpcLock, NULL)) == NULL) {
  112. Prcb->DpcCount += 1;
  113. Prcb->DpcQueueDepth += 1;
  114. Dpc->SystemArgument1 = SystemArgument1;
  115. Dpc->SystemArgument2 = SystemArgument2;
  116. //
  117. // If the DPC is of high importance, then insert the DPC at the
  118. // head of the DPC queue. Otherwise, insert the DPC at the end
  119. // of the DPC queue.
  120. //
  121. if (Dpc->Importance == HighImportance) {
  122. InsertHeadList(&Prcb->DpcListHead, &Dpc->DpcListEntry);
  123. } else {
  124. InsertTailList(&Prcb->DpcListHead, &Dpc->DpcListEntry);
  125. }
  126. //
  127. // If a DPC routine is not active on the target processor, then
  128. // request a dispatch interrupt if appropriate.
  129. //
  130. if ((Prcb->DpcRoutineActive == FALSE) &&
  131. (Prcb->DpcInterruptRequested == FALSE)) {
  132. //
  133. // Request a dispatch interrupt on the current processor if
  134. // the DPC is not of low importance, the length of the DPC
  135. // queue has exceeded the maximum threshold, or if the DPC
  136. // request rate is below the minimum threshold.
  137. //
  138. #if defined(NT_UP)
  139. if ((Dpc->Importance != LowImportance) ||
  140. (Prcb->DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) ||
  141. (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) {
  142. Prcb->DpcInterruptRequested = TRUE;
  143. KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
  144. }
  145. //
  146. // If the DPC is being queued to another processor and the
  147. // DPC is of high importance, or the length of the other
  148. // processor's DPC queue has exceeded the maximum threshold,
  149. // then request a dispatch interrupt.
  150. //
  151. #else
  152. if (Prcb != KeGetCurrentPrcb()) {
  153. if (((Dpc->Importance == HighImportance) ||
  154. (Prcb->DpcQueueDepth >= Prcb->MaximumDpcQueueDepth))) {
  155. Prcb->DpcInterruptRequested = TRUE;
  156. KiIpiSend(AFFINITY_MASK(Processor), IPI_DPC);
  157. }
  158. } else {
  159. //
  160. // Request a dispatch interrupt on the current processor if
  161. // the DPC is not of low importance, the length of the DPC
  162. // queue has exceeded the maximum threshold, or if the DPC
  163. // request rate is below the minimum threshold.
  164. //
  165. if ((Dpc->Importance != LowImportance) ||
  166. (Prcb->DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) ||
  167. (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) {
  168. Prcb->DpcInterruptRequested = TRUE;
  169. KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
  170. }
  171. }
  172. #endif
  173. }
  174. }
  175. //
  176. // Release the DPC lock, enable interrupts, and return whether the
  177. // DPC was queued or not.
  178. //
  179. #if !defined(NT_UP)
  180. KiReleaseSpinLock(&Prcb->DpcLock);
  181. #endif
  182. KeLowerIrql(OldIrql);
  183. return (Lock == NULL);
  184. }
  185. BOOLEAN
  186. KeRemoveQueueDpc (
  187. IN PRKDPC Dpc
  188. )
  189. /*++
  190. Routine Description:
  191. This function removes a DPC object from the DPC queue. If the DPC object
  192. is not in the DPC queue, then no operation is performed. Otherwise, the
  193. DPC object is removed from the DPC queue and its inserted state is set
  194. FALSE.
  195. Arguments:
  196. Dpc - Supplies a pointer to a control object of type DPC.
  197. Return Value:
  198. If the DPC object is not in the DPC queue, then a value of FALSE is
  199. returned. Otherwise a value of TRUE is returned.
  200. --*/
  201. {
  202. PKSPIN_LOCK Lock;
  203. PKPRCB Prcb;
  204. ASSERT_DPC(Dpc);
  205. //
  206. // If the DPC object is in the DPC queue, then remove it from the queue
  207. // and set its inserted state to FALSE.
  208. //
  209. _disable();
  210. Lock = Dpc->Lock;
  211. if (Lock != NULL) {
  212. //
  213. // Acquire the DPC lock of the target processor.
  214. //
  215. #if !defined(NT_UP)
  216. KiAcquireSpinLock(Lock);
  217. #endif
  218. //
  219. // If the specified DPC is still in the DPC queue, then remove
  220. // it.
  221. //
  222. // N.B. It is possible for specified DPC to be removed from the
  223. // specified DPC queue before the DPC lock is obtained.
  224. //
  225. //
  226. if (Lock == Dpc->Lock) {
  227. Prcb = CONTAINING_RECORD(Lock, KPRCB, DpcLock);
  228. Prcb->DpcQueueDepth -= 1;
  229. RemoveEntryList(&Dpc->DpcListEntry);
  230. Dpc->Lock = NULL;
  231. }
  232. //
  233. // Release the DPC lock of the target processor.
  234. //
  235. #if !defined(NT_UP)
  236. KiReleaseSpinLock(Lock);
  237. #endif
  238. }
  239. //
  240. // Enable interrupts and return whether the DPC was removed from a DPC
  241. // queue.
  242. //
  243. _enable();
  244. return (Lock != NULL);
  245. }
  246. VOID
  247. KeSetImportanceDpc (
  248. IN PRKDPC Dpc,
  249. IN KDPC_IMPORTANCE Importance
  250. )
  251. /*++
  252. Routine Description:
  253. This function sets the importance of a DPC.
  254. Arguments:
  255. Dpc - Supplies a pointer to a control object of type DPC.
  256. Number - Supplies the importance of the DPC.
  257. Return Value:
  258. None.
  259. --*/
  260. {
  261. //
  262. // Set the importance of the DPC.
  263. //
  264. Dpc->Importance = (UCHAR)Importance;
  265. return;
  266. }
  267. VOID
  268. KeSetTargetProcessorDpc (
  269. IN PRKDPC Dpc,
  270. IN CCHAR Number
  271. )
  272. /*++
  273. Routine Description:
  274. This function sets the processor number to which the DPC is targeted.
  275. Arguments:
  276. Dpc - Supplies a pointer to a control object of type DPC.
  277. Number - Supplies the target processor number.
  278. Return Value:
  279. None.
  280. --*/
  281. {
  282. //
  283. // Set target processor number.
  284. //
  285. // The target processor number if biased by the maximum number of
  286. // processors that are supported.
  287. //
  288. Dpc->Number = MAXIMUM_PROCESSORS + Number;
  289. return;
  290. }
  291. VOID
  292. KeFlushQueuedDpcs(
  293. VOID
  294. )
  295. /*++
  296. Routine Description:
  297. This function causes all current DPCs on all processors to execute to completion.
  298. This is used at driver unload to make sure all driver DPC processing has exited the
  299. driver image before the code and data is deleted
  300. Arguments:
  301. None.
  302. Return Value:
  303. None.
  304. --*/
  305. {
  306. #if !defined(NT_UP)
  307. PKTHREAD CurrentThread;
  308. KPRIORITY OldPriority;
  309. KAFFINITY ProcessorMask;
  310. BOOLEAN SetAffinity;
  311. #endif
  312. PAGED_CODE ();
  313. #if !defined(NT_UP)
  314. //
  315. // Set the priority of this thread high so we will always run on the target processor
  316. //
  317. CurrentThread = KeGetCurrentThread();
  318. OldPriority = KeSetPriorityThread(CurrentThread, HIGH_PRIORITY);
  319. ProcessorMask = KeActiveProcessors;
  320. SetAffinity = FALSE;
  321. //
  322. // Subtract the processor we are running on now from the affinity mask and let the
  323. // kernel select the next processor we can run on.
  324. //
  325. ProcessorMask &= ~(AFFINITY_MASK(KeGetCurrentProcessorNumber()));
  326. while (ProcessorMask) {
  327. KeSetSystemAffinityThread(ProcessorMask);
  328. SetAffinity = TRUE;
  329. ProcessorMask &= ~(AFFINITY_MASK(KeGetCurrentProcessorNumber()));
  330. }
  331. //
  332. // Put affinity and priority back as it was
  333. //
  334. if (SetAffinity) {
  335. KeRevertToUserAffinityThread ();
  336. }
  337. OldPriority = KeSetPriorityThread(CurrentThread, OldPriority);
  338. #endif
  339. }