ipi.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /*++
  2. Copyright (c) 2012 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. ipi.c
  9. Abstract:
  10. This module implements support for generic Inter-Processor Interrupt
  11. handling.
  12. Author:
  13. Evan Green 27-Sep-2012
  14. Environment:
  15. Kernel
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #include <minoca/kernel/kernel.h>
  21. #include "kep.h"
  22. //
  23. // ---------------------------------------------------------------- Definitions
  24. //
  25. #define IPI_ALLOCATION_TAG 0x6970494B // 'ipIK'
  26. //
  27. // ------------------------------------------------------ Data Type Definitions
  28. //
  29. //
  30. // ----------------------------------------------- Internal Function Prototypes
  31. //
  32. INTERRUPT_STATUS
  33. KeIpiServiceRoutine (
  34. PVOID Context
  35. );
  36. //
  37. // -------------------------------------------------------------------- Globals
  38. //
  39. //
  40. // ------------------------------------------------------------------ Functions
  41. //
  42. PPROCESSOR_BLOCK
  43. KeGetProcessorBlock (
  44. ULONG ProcessorNumber
  45. )
  46. /*++
  47. Routine Description:
  48. This routine returns the processor block for the given processor number.
  49. Arguments:
  50. ProcessorNumber - Supplies the number of the processor.
  51. Return Value:
  52. Returns the processor block for the given processor.
  53. NULL if the input was not a valid processor number.
  54. --*/
  55. {
  56. if (ProcessorNumber >= KeActiveProcessorCount) {
  57. return NULL;
  58. }
  59. return KeProcessorBlocks[ProcessorNumber];
  60. }
  61. KSTATUS
  62. KeSendIpi (
  63. PIPI_ROUTINE IpiRoutine,
  64. PVOID IpiContext,
  65. PPROCESSOR_SET Processors
  66. )
  67. /*++
  68. Routine Description:
  69. This routine runs the given routine at IPI level on the specified set of
  70. processors. This routine runs synchronously: the routine will have completed
  71. running on all processors by the time this routine returns. This routine
  72. must be called at or below dispatch level.
  73. Arguments:
  74. IpiRoutine - Supplies a pointer to the routine to run at IPI level.
  75. IpiContext - Supplies the value to pass to the IPI routine as a parameter.
  76. Processors - Supplies the set of processors to run the IPI on.
  77. Return Value:
  78. Status code.
  79. --*/
  80. {
  81. ULONG CurrentProcessor;
  82. PIPI_REQUEST IpiRequests;
  83. RUNLEVEL OldRunLevel;
  84. PPROCESSOR_BLOCK Processor;
  85. ULONG ProcessorCount;
  86. ULONG ProcessorIndex;
  87. volatile ULONG ProcessorsRemaining;
  88. KSTATUS Status;
  89. ASSERT(KeGetRunLevel() <= RunLevelDispatch);
  90. CurrentProcessor = KeGetCurrentProcessorNumber();
  91. IpiRequests = NULL;
  92. if (Processors == NULL) {
  93. Status = STATUS_INVALID_PARAMETER;
  94. goto SendIpiEnd;
  95. }
  96. //
  97. // Determine the number of processors targeted by this IPI.
  98. //
  99. switch (Processors->Target) {
  100. case ProcessorTargetNone:
  101. Status = STATUS_SUCCESS;
  102. goto SendIpiEnd;
  103. case ProcessorTargetAll:
  104. ProcessorCount = KeActiveProcessorCount;
  105. break;
  106. case ProcessorTargetAllExcludingSelf:
  107. ProcessorCount = KeActiveProcessorCount - 1;
  108. break;
  109. case ProcessorTargetSelf:
  110. ProcessorCount = 1;
  111. break;
  112. case ProcessorTargetSingleProcessor:
  113. ProcessorCount = 1;
  114. break;
  115. case ProcessorTargetAny:
  116. default:
  117. ASSERT(FALSE);
  118. Status = STATUS_INVALID_PARAMETER;
  119. goto SendIpiEnd;
  120. }
  121. //
  122. // Allocate an IPI request packet for each processor being targeted.
  123. //
  124. IpiRequests = MmAllocateNonPagedPool(ProcessorCount * sizeof(IPI_REQUEST),
  125. IPI_ALLOCATION_TAG);
  126. if (IpiRequests == NULL) {
  127. Status = STATUS_INSUFFICIENT_RESOURCES;
  128. goto SendIpiEnd;
  129. }
  130. //
  131. // Fill out each IPI request packet.
  132. //
  133. for (ProcessorIndex = 0;
  134. ProcessorIndex < ProcessorCount;
  135. ProcessorIndex += 1) {
  136. IpiRequests[ProcessorIndex].IpiRoutine = IpiRoutine;
  137. IpiRequests[ProcessorIndex].Context = IpiContext;
  138. IpiRequests[ProcessorIndex].ProcessorsRemaining = &ProcessorsRemaining;
  139. }
  140. //
  141. // Put each IPI request packet on the given processor blocks.
  142. //
  143. OldRunLevel = KeRaiseRunLevel(RunLevelIpi);
  144. ProcessorsRemaining = ProcessorCount;
  145. switch (Processors->Target) {
  146. case ProcessorTargetAll:
  147. case ProcessorTargetAllExcludingSelf:
  148. //
  149. // Insert the IPI onto each processor's list.
  150. //
  151. for (ProcessorIndex = 0;
  152. ProcessorIndex < ProcessorCount;
  153. ProcessorIndex += 1) {
  154. Processor = KeProcessorBlocks[ProcessorIndex];
  155. //
  156. // If the IPI is not targeted at this processor, skip it.
  157. //
  158. if ((ProcessorIndex == CurrentProcessor) &&
  159. (Processors->Target == ProcessorTargetAllExcludingSelf)) {
  160. continue;
  161. }
  162. //
  163. // Insert the IPI onto the end of the processor's IPI request
  164. // queue.
  165. //
  166. KeAcquireSpinLock(&(Processor->IpiListLock));
  167. INSERT_BEFORE(&(IpiRequests[ProcessorIndex].ListEntry),
  168. &(Processor->IpiListHead));
  169. KeReleaseSpinLock(&(Processor->IpiListLock));
  170. }
  171. break;
  172. case ProcessorTargetSelf:
  173. //
  174. // Insert the IPI request onto this processor's queue, then flush it.
  175. //
  176. Processor = KeProcessorBlocks[CurrentProcessor];
  177. KeAcquireSpinLock(&(Processor->IpiListLock));
  178. INSERT_BEFORE(&(IpiRequests[0].ListEntry), &(Processor->IpiListHead));
  179. KeReleaseSpinLock(&(Processor->IpiListLock));
  180. break;
  181. case ProcessorTargetSingleProcessor:
  182. if (Processors->U.Number > KeActiveProcessorCount) {
  183. Status = STATUS_INVALID_PARAMETER;
  184. goto SendIpiEnd;
  185. }
  186. Processor = KeProcessorBlocks[Processors->U.Number];
  187. KeAcquireSpinLock(&(Processor->IpiListLock));
  188. INSERT_BEFORE(&(IpiRequests[0].ListEntry), &(Processor->IpiListHead));
  189. KeReleaseSpinLock(&(Processor->IpiListLock));
  190. break;
  191. case ProcessorTargetAny:
  192. default:
  193. ASSERT(FALSE);
  194. Status = STATUS_INVALID_PARAMETER;
  195. KeLowerRunLevel(OldRunLevel);
  196. goto SendIpiEnd;
  197. }
  198. //
  199. // Send the IPI interrupt, unless there is only one processor in the system,
  200. // in which case just call the routine.
  201. //
  202. if ((ProcessorCount == 1) && (KeActiveProcessorCount == 1)) {
  203. KeIpiServiceRoutine(NULL);
  204. Status = STATUS_SUCCESS;
  205. } else {
  206. Status = HlSendIpi(IpiTypePacket, Processors);
  207. }
  208. KeLowerRunLevel(OldRunLevel);
  209. if (!KSUCCESS(Status)) {
  210. goto SendIpiEnd;
  211. }
  212. //
  213. // Wait for all processors to complete the IPI.
  214. //
  215. while (ProcessorsRemaining != 0) {
  216. ArProcessorYield();
  217. }
  218. Status = STATUS_SUCCESS;
  219. SendIpiEnd:
  220. if (IpiRequests != NULL) {
  221. MmFreeNonPagedPool(IpiRequests);
  222. }
  223. return Status;
  224. }
  225. INTERRUPT_STATUS
  226. KeIpiServiceRoutine (
  227. PVOID Context
  228. )
  229. /*++
  230. Routine Description:
  231. This routine checks for any pending IPIs on the current processor and
  232. executes them, in order. The processor must be executing at IPI level.
  233. Arguments:
  234. Context - Supplies an unused context pointer.
  235. Return Value:
  236. Returns claimed always. On return, the IPI queue will be empty.
  237. --*/
  238. {
  239. PIPI_REQUEST CurrentRequest;
  240. RUNLEVEL OldRunLevel;
  241. PPROCESSOR_BLOCK Processor;
  242. OldRunLevel = KeRaiseRunLevel(RunLevelIpi);
  243. Processor = KeGetCurrentProcessorBlock();
  244. KeAcquireSpinLock(&(Processor->IpiListLock));
  245. while (!LIST_EMPTY(&(Processor->IpiListHead))) {
  246. //
  247. // Get and remove the first item on the list.
  248. //
  249. CurrentRequest = LIST_VALUE(Processor->IpiListHead.Next,
  250. IPI_REQUEST,
  251. ListEntry);
  252. LIST_REMOVE(&(CurrentRequest->ListEntry));
  253. //
  254. // Execute the IPI.
  255. //
  256. CurrentRequest->IpiRoutine(CurrentRequest->Context);
  257. //
  258. // Signal this IPI is complete.
  259. //
  260. RtlAtomicAdd32(CurrentRequest->ProcessorsRemaining, -1);
  261. }
  262. KeReleaseSpinLock(&(Processor->IpiListLock));
  263. KeLowerRunLevel(OldRunLevel);
  264. return InterruptStatusClaimed;
  265. }
  266. //
  267. // --------------------------------------------------------- Internal Functions
  268. //