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.

413 lines
12 KiB

  1. /*** sched.c - AML thread scheduler
  2. *
  3. * Copyright (c) 1996,1998 Microsoft Corporation
  4. * Author: Michael Tsang (MikeTs)
  5. * Created 03/04/98
  6. *
  7. * MODIFICATION HISTORY
  8. */
  9. #include "pch.h"
  10. #ifdef LOCKABLE_PRAGMA
  11. #pragma ACPI_LOCKABLE_DATA
  12. #pragma ACPI_LOCKABLE_CODE
  13. #endif
  14. /***LP ExpireTimeSlice - DPC callback for time slice expiration
  15. *
  16. * ENTRY
  17. * pkdpc -> DPC
  18. * pctxtq -> CTXTQ
  19. * SysArg1 - not used
  20. * SysArg2 - not used
  21. *
  22. * EXIT
  23. * None
  24. */
  25. VOID ExpireTimeSlice(PKDPC pkdpc, PCTXTQ pctxtq, PVOID SysArg1, PVOID SysArg2)
  26. {
  27. TRACENAME("EXPIRETIMESLICE")
  28. ENTER(2, ("ExpireTimeSlice(pkdpc=%x,pctxtq=%x,SysArg1=%x,SysArg2=%x\n",
  29. pkdpc, pctxtq, SysArg1, SysArg2));
  30. DEREF(pkdpc);
  31. DEREF(SysArg1);
  32. DEREF(SysArg2);
  33. pctxtq->dwfCtxtQ |= CQF_TIMESLICE_EXPIRED;
  34. EXIT(2, ("ExpireTimeSlice!\n"));
  35. } //ExpireTimeSlice
  36. /***LP StartTimeSlice - Timer callback to start a new time slice
  37. *
  38. * ENTRY
  39. * pkdpc -> DPC
  40. * pctxtq -> CTXTQ
  41. * SysArg1 - not used
  42. * SysArg2 - not used
  43. *
  44. * EXIT
  45. * None
  46. */
  47. VOID StartTimeSlice(PKDPC pkdpc, PCTXTQ pctxtq, PVOID SysArg1, PVOID SysArg2)
  48. {
  49. TRACENAME("STARTTIMESLICE")
  50. ENTER(2, ("StartTimeSlice(pkdpc=%x,pctxtq=%x,SysArg1=%x,SysArg2=%x\n",
  51. pkdpc, pctxtq, SysArg1, SysArg2));
  52. DEREF(pkdpc);
  53. DEREF(SysArg1);
  54. DEREF(SysArg2);
  55. //
  56. // If somebody has restarted the queue, we don't have do anything.
  57. //
  58. ASSERT(pctxtq->plistCtxtQ != NULL);
  59. if ((pctxtq->plistCtxtQ != NULL) &&
  60. !(pctxtq->dwfCtxtQ & CQF_WORKITEM_SCHEDULED))
  61. {
  62. OSQueueWorkItem(&pctxtq->WorkItem);
  63. pctxtq->dwfCtxtQ |= CQF_WORKITEM_SCHEDULED;
  64. }
  65. EXIT(2, ("StartTimeSlice!\n"));
  66. } //StartTimeSlice
  67. /***LP StartTimeSlicePassive - Start a time slice at PASSIVE_LEVEL
  68. *
  69. * ENTRY
  70. * pctxtq -> CTXTQ
  71. *
  72. * EXIT
  73. * None
  74. */
  75. VOID StartTimeSlicePassive(PCTXTQ pctxtq)
  76. {
  77. TRACENAME("STARTTIMESLICEPASSIVE")
  78. ENTER(2, ("StartTimeSlicePassive(pctxtq=%x)\n", pctxtq));
  79. AcquireMutex(&pctxtq->mutCtxtQ);
  80. pctxtq->dwfCtxtQ &= ~CQF_WORKITEM_SCHEDULED;
  81. //
  82. // Make sure there is something in the queue and no current active context.
  83. //
  84. if ((pctxtq->plistCtxtQ != NULL) && (pctxtq->pkthCurrent == NULL) &&
  85. !(pctxtq->dwfCtxtQ & CQF_PAUSED))
  86. {
  87. DispatchCtxtQueue(pctxtq);
  88. }
  89. ReleaseMutex(&pctxtq->mutCtxtQ);
  90. EXIT(2, ("StartTimeSlicePassive!\n"));
  91. } //StartTimeSlicePassive
  92. /***LP DispatchCtxtQueue - Dispatch context from ready queue
  93. *
  94. * ENTRY
  95. * pctxtq -> CTXTQ
  96. *
  97. * EXIT
  98. * None
  99. *
  100. * Note
  101. * The caller must acquire CtxtQ mutex before entering this routine.
  102. */
  103. VOID LOCAL DispatchCtxtQueue(PCTXTQ pctxtq)
  104. {
  105. TRACENAME("DISPATCHCTXTQUEUE")
  106. LARGE_INTEGER liTimeout;
  107. PLIST plist;
  108. PCTXT pctxt;
  109. ENTER(2, ("DispatchCtxtQueue(pctxtq=%x)\n", pctxtq));
  110. ASSERT((pctxtq->plistCtxtQ != NULL) && (pctxtq->pkthCurrent == NULL));
  111. liTimeout.QuadPart = (INT_PTR)(-10000*(INT_PTR)pctxtq->dwmsTimeSliceLength);
  112. pctxtq->dwfCtxtQ &= ~CQF_TIMESLICE_EXPIRED;
  113. KeSetTimer(&pctxtq->Timer, liTimeout, &pctxtq->DpcExpireTimeSlice);
  114. while ((plist = ListRemoveHead(&pctxtq->plistCtxtQ)) != NULL)
  115. {
  116. pctxt = CONTAINING_RECORD(plist, CTXT, listQueue);
  117. ASSERT(pctxt->pplistCtxtQueue == &pctxtq->plistCtxtQ);
  118. pctxt->pplistCtxtQueue = NULL;
  119. pctxt->dwfCtxt &= ~CTXTF_IN_READYQ;
  120. RunContext(pctxt);
  121. }
  122. if (pctxtq->plistCtxtQ == NULL)
  123. {
  124. KeCancelTimer(&pctxtq->Timer);
  125. pctxtq->dwfCtxtQ &= ~CQF_TIMESLICE_EXPIRED;
  126. }
  127. else if (!(pctxtq->dwfCtxtQ & CQF_WORKITEM_SCHEDULED))
  128. {
  129. //
  130. // Our time slice has expired, reschedule another time slice if not
  131. // already done so.
  132. //
  133. liTimeout.QuadPart = (INT_PTR)(-10000*(INT_PTR)pctxtq->dwmsTimeSliceInterval);
  134. KeSetTimer(&pctxtq->Timer, liTimeout, &pctxtq->DpcStartTimeSlice);
  135. }
  136. EXIT(2, ("DispatchCtxtQueue!\n"));
  137. } //DispatchCtxtQueue
  138. /***LP InsertReadyQueue - Insert the context into the ready queue
  139. *
  140. * ENTRY
  141. * pctxt -> CTXT
  142. * fDelayExecute - queue the request, don't execute now
  143. *
  144. * EXIT-SUCCESS
  145. * returns STATUS_SUCCESS
  146. * EXIT-FAILURE
  147. * returns AMLIERR_ code
  148. *
  149. * NOTE
  150. * The caller must acquire the CtxtQ mutex before entering this
  151. * routine and release it after exiting this routine.
  152. */
  153. NTSTATUS LOCAL InsertReadyQueue(PCTXT pctxt, BOOLEAN fDelayExecute)
  154. {
  155. TRACENAME("INSERTREADYQUEUE")
  156. NTSTATUS rc = STATUS_SUCCESS;
  157. ENTER(2, ("InsertReadyQueue(pctxt=%x,fDelayExecute=%x)\n",
  158. pctxt, fDelayExecute));
  159. CHKDEBUGGERREQ();
  160. //
  161. // Make sure we do have the spin lock.
  162. //
  163. LOGSCHEDEVENT('INSQ', (ULONG_PTR)pctxt, (ULONG_PTR)
  164. (pctxt->pnctxt? pctxt->pnctxt->pnsObj: pctxt->pnsObj),
  165. (ULONG_PTR)pctxt->pbOp);
  166. //
  167. // If there is a pending timer, cancel it.
  168. //
  169. if (pctxt->dwfCtxt & CTXTF_TIMER_PENDING)
  170. {
  171. BOOLEAN fTimerCancelled;
  172. pctxt->dwfCtxt &= ~CTXTF_TIMER_PENDING;
  173. fTimerCancelled = KeCancelTimer(&pctxt->Timer);
  174. //
  175. // If the timer could not be cancelled (already queued), wait
  176. // for it to fire and dispatch the context from there. The
  177. // pending timer is referring to this context and we can not
  178. // have it completed with the timer outstanding. Plus this
  179. // also interlocked to setting of timers and timeout processing
  180. // to ensure that a timeout is not mistakenly performed on
  181. // the next timer.
  182. //
  183. if (!fTimerCancelled)
  184. {
  185. pctxt->dwfCtxt |= CTXTF_TIMER_DISPATCH;
  186. }
  187. }
  188. //
  189. // Make this context ready.
  190. //
  191. pctxt->dwfCtxt |= CTXTF_READY;
  192. //
  193. // If this context is already running, we are done; otherwise, process it.
  194. //
  195. if (!(pctxt->dwfCtxt & CTXTF_TIMER_DISPATCH) &&
  196. (!(pctxt->dwfCtxt & CTXTF_RUNNING) ||
  197. (pctxt->dwfCtxt & CTXTF_NEST_EVAL)))
  198. {
  199. if (fDelayExecute)
  200. {
  201. //
  202. // This context is from a completion callback of current context,
  203. // we need to unblock/restart current context.
  204. //
  205. ReleaseMutex(&gReadyQueue.mutCtxtQ);
  206. AsyncCallBack(pctxt, AMLISTA_CONTINUE);
  207. AcquireMutex(&gReadyQueue.mutCtxtQ);
  208. }
  209. else if ((pctxt->dwfCtxt & CTXTF_NEST_EVAL) &&
  210. (gReadyQueue.pkthCurrent == KeGetCurrentThread()))
  211. {
  212. LOGSCHEDEVENT('NEST', (ULONG_PTR)pctxt, (ULONG_PTR)
  213. (pctxt->pnctxt? pctxt->pnctxt->pnsObj: pctxt->pnsObj),
  214. (ULONG_PTR)pctxt->pbOp);
  215. //
  216. // Somebody is running a new method on the callout of the current
  217. // context. We must run this new context first or else we will
  218. // dead lock the current context. We assume that if pending is
  219. // returned, the callout will return.
  220. //
  221. rc = RunContext(pctxt);
  222. }
  223. else if ((gReadyQueue.pkthCurrent == NULL) &&
  224. !(gReadyQueue.dwfCtxtQ & CQF_PAUSED))
  225. //
  226. // We only execute the method if we are not in paused state.
  227. //
  228. {
  229. LOGSCHEDEVENT('EVAL', (ULONG_PTR)pctxt, (ULONG_PTR)
  230. (pctxt->pnctxt? pctxt->pnctxt->pnsObj: pctxt->pnsObj),
  231. (ULONG_PTR)pctxt->pbOp);
  232. //
  233. // There is no active context and we can execute it immediately.
  234. //
  235. rc = RunContext(pctxt);
  236. if ((gReadyQueue.plistCtxtQ != NULL) &&
  237. !(gReadyQueue.dwfCtxtQ & CQF_WORKITEM_SCHEDULED))
  238. {
  239. //
  240. // If we have more jobs in the queue and we haven't scheduled
  241. // a dispatch, schedule one.
  242. //
  243. LOGSCHEDEVENT('KICK', (ULONG_PTR)rc, 0, 0);
  244. OSQueueWorkItem(&gReadyQueue.WorkItem);
  245. gReadyQueue.dwfCtxtQ |= CQF_WORKITEM_SCHEDULED;
  246. }
  247. }
  248. else
  249. {
  250. //
  251. // Insert the context in the ready queue.
  252. //
  253. ASSERT(!(pctxt->dwfCtxt & (CTXTF_IN_READYQ | CTXTF_RUNNING)));
  254. LOGSCHEDEVENT('QCTX', (ULONG_PTR)pctxt, (ULONG_PTR)
  255. (pctxt->pnctxt? pctxt->pnctxt->pnsObj: pctxt->pnsObj),
  256. (ULONG_PTR)pctxt->pbOp);
  257. if (!(pctxt->dwfCtxt & CTXTF_IN_READYQ))
  258. {
  259. pctxt->dwfCtxt |= CTXTF_IN_READYQ;
  260. ListInsertTail(&pctxt->listQueue, &gReadyQueue.plistCtxtQ);
  261. pctxt->pplistCtxtQueue = &gReadyQueue.plistCtxtQ;
  262. }
  263. pctxt->dwfCtxt |= CTXTF_NEED_CALLBACK;
  264. rc = AMLISTA_PENDING;
  265. }
  266. }
  267. EXIT(2, ("InsertReadyQueue=%x\n", rc));
  268. return rc;
  269. } //InsertReadyQueue
  270. /***LP RestartContext - Restart a context
  271. *
  272. * ENTRY
  273. * pctxt -> CTXT structure
  274. * fDelayExecute - TRUE to queue for delay execution
  275. *
  276. * EXIT-SUCCESS
  277. * returns STATUS_SUCCESS
  278. * EXIT-FAILURE
  279. * returns AMLIERR_ code
  280. * None
  281. */
  282. NTSTATUS LOCAL RestartContext(PCTXT pctxt, BOOLEAN fDelayExecute)
  283. {
  284. TRACENAME("RESTARTCONTEXT")
  285. NTSTATUS rc = STATUS_SUCCESS;
  286. PRESTART prest;
  287. ENTER(2, ("RestartContext(pctxt=%x,fDelayExecute=%x)\n",
  288. pctxt, fDelayExecute));
  289. ASSERT(!(pctxt->dwfCtxt & CTXTF_TIMER_PENDING));
  290. ASSERT((fDelayExecute == FALSE) || !(pctxt->dwfCtxt & CTXTF_ASYNC_EVAL));
  291. LOGSCHEDEVENT('REST', (ULONG_PTR)pctxt, (ULONG_PTR)
  292. (pctxt->pnctxt? pctxt->pnctxt->pnsObj: pctxt->pnsObj),
  293. (ULONG_PTR)pctxt->pbOp);
  294. if (KeGetCurrentIrql() < DISPATCH_LEVEL)
  295. {
  296. AcquireMutex(&gReadyQueue.mutCtxtQ);
  297. rc = InsertReadyQueue(pctxt, fDelayExecute);
  298. ReleaseMutex(&gReadyQueue.mutCtxtQ);
  299. }
  300. else if ((prest = NEWRESTOBJ(sizeof(RESTART))) != NULL)
  301. {
  302. pctxt->dwfCtxt |= CTXTF_NEED_CALLBACK;
  303. prest->pctxt = pctxt;
  304. ExInitializeWorkItem(&prest->WorkItem, RestartCtxtPassive, prest);
  305. OSQueueWorkItem(&prest->WorkItem);
  306. rc = AMLISTA_PENDING;
  307. }
  308. else
  309. {
  310. rc = AMLI_LOGERR(AMLIERR_FATAL,
  311. ("RestartContext: failed to allocate restart context item"));
  312. }
  313. EXIT(2, ("RestartContext=%x\n", rc));
  314. return rc;
  315. } //RestartContext
  316. /***LP RestartCtxtPassive - Restart context running at PASSIVE_LEVEL
  317. *
  318. * ENTRY
  319. * prest-> RESTART
  320. *
  321. * EXIT
  322. * None
  323. */
  324. VOID RestartCtxtPassive(PRESTART prest)
  325. {
  326. TRACENAME("RESTARTCTXTPASSIVE")
  327. ENTER(2, ("RestartCtxtPassive(prest=%x)\n", prest));
  328. AcquireMutex(&gReadyQueue.mutCtxtQ);
  329. InsertReadyQueue(prest->pctxt,
  330. (BOOLEAN)((prest->pctxt->dwfCtxt & CTXTF_ASYNC_EVAL) == 0));
  331. ReleaseMutex(&gReadyQueue.mutCtxtQ);
  332. FREERESTOBJ(prest);
  333. EXIT(2, ("RestartCtxtPassive!\n"));
  334. } //RestartCtxtPassive
  335. /***LP RestartCtxtCallback - Callback to restart a context
  336. *
  337. * ENTRY
  338. * pctxtdata -> CTXTDATA structure
  339. *
  340. * EXIT
  341. * None
  342. */
  343. VOID EXPORT RestartCtxtCallback(PCTXTDATA pctxtdata)
  344. {
  345. TRACENAME("RESTARTCTXTCALLBACK")
  346. PCTXT pctxt = CONTAINING_RECORD(pctxtdata, CTXT, CtxtData);
  347. ENTER(2, ("RestartCtxtCallback(pctxt=%x)\n", pctxt));
  348. ASSERT(pctxt->dwSig == SIG_CTXT);
  349. LOGSCHEDEVENT('RSCB', (ULONG_PTR)pctxt, 0, 0);
  350. RestartContext(pctxt,
  351. (BOOLEAN)((pctxt->dwfCtxt & CTXTF_ASYNC_EVAL) == 0));
  352. EXIT(2, ("RestartCtxtCallback!\n"));
  353. } //RestartCtxtCallback