|
|
/*** sched.c - AML thread scheduler
* * Copyright (c) 1996,1998 Microsoft Corporation * Author: Michael Tsang (MikeTs) * Created 03/04/98 * * MODIFICATION HISTORY */
#include "pch.h"
#ifdef LOCKABLE_PRAGMA
#pragma ACPI_LOCKABLE_DATA
#pragma ACPI_LOCKABLE_CODE
#endif
/***LP ExpireTimeSlice - DPC callback for time slice expiration
* * ENTRY * pkdpc -> DPC * pctxtq -> CTXTQ * SysArg1 - not used * SysArg2 - not used * * EXIT * None */
VOID ExpireTimeSlice(PKDPC pkdpc, PCTXTQ pctxtq, PVOID SysArg1, PVOID SysArg2) { TRACENAME("EXPIRETIMESLICE")
ENTER(2, ("ExpireTimeSlice(pkdpc=%x,pctxtq=%x,SysArg1=%x,SysArg2=%x\n", pkdpc, pctxtq, SysArg1, SysArg2));
DEREF(pkdpc); DEREF(SysArg1); DEREF(SysArg2);
pctxtq->dwfCtxtQ |= CQF_TIMESLICE_EXPIRED;
EXIT(2, ("ExpireTimeSlice!\n")); } //ExpireTimeSlice
/***LP StartTimeSlice - Timer callback to start a new time slice
* * ENTRY * pkdpc -> DPC * pctxtq -> CTXTQ * SysArg1 - not used * SysArg2 - not used * * EXIT * None */
VOID StartTimeSlice(PKDPC pkdpc, PCTXTQ pctxtq, PVOID SysArg1, PVOID SysArg2) { TRACENAME("STARTTIMESLICE")
ENTER(2, ("StartTimeSlice(pkdpc=%x,pctxtq=%x,SysArg1=%x,SysArg2=%x\n", pkdpc, pctxtq, SysArg1, SysArg2));
DEREF(pkdpc); DEREF(SysArg1); DEREF(SysArg2);
//
// If somebody has restarted the queue, we don't have do anything.
//
ASSERT(pctxtq->plistCtxtQ != NULL);
if ((pctxtq->plistCtxtQ != NULL) && !(pctxtq->dwfCtxtQ & CQF_WORKITEM_SCHEDULED)) { OSQueueWorkItem(&pctxtq->WorkItem); pctxtq->dwfCtxtQ |= CQF_WORKITEM_SCHEDULED; }
EXIT(2, ("StartTimeSlice!\n")); } //StartTimeSlice
/***LP StartTimeSlicePassive - Start a time slice at PASSIVE_LEVEL
* * ENTRY * pctxtq -> CTXTQ * * EXIT * None */
VOID StartTimeSlicePassive(PCTXTQ pctxtq) { TRACENAME("STARTTIMESLICEPASSIVE")
ENTER(2, ("StartTimeSlicePassive(pctxtq=%x)\n", pctxtq));
AcquireMutex(&pctxtq->mutCtxtQ);
pctxtq->dwfCtxtQ &= ~CQF_WORKITEM_SCHEDULED; //
// Make sure there is something in the queue and no current active context.
//
if ((pctxtq->plistCtxtQ != NULL) && (pctxtq->pkthCurrent == NULL) && !(pctxtq->dwfCtxtQ & CQF_PAUSED)) { DispatchCtxtQueue(pctxtq); }
ReleaseMutex(&pctxtq->mutCtxtQ);
EXIT(2, ("StartTimeSlicePassive!\n")); } //StartTimeSlicePassive
/***LP DispatchCtxtQueue - Dispatch context from ready queue
* * ENTRY * pctxtq -> CTXTQ * * EXIT * None * * Note * The caller must acquire CtxtQ mutex before entering this routine. */
VOID LOCAL DispatchCtxtQueue(PCTXTQ pctxtq) { TRACENAME("DISPATCHCTXTQUEUE") LARGE_INTEGER liTimeout; PLIST plist; PCTXT pctxt;
ENTER(2, ("DispatchCtxtQueue(pctxtq=%x)\n", pctxtq));
ASSERT((pctxtq->plistCtxtQ != NULL) && (pctxtq->pkthCurrent == NULL));
liTimeout.QuadPart = (INT_PTR)(-10000*(INT_PTR)pctxtq->dwmsTimeSliceLength); pctxtq->dwfCtxtQ &= ~CQF_TIMESLICE_EXPIRED; KeSetTimer(&pctxtq->Timer, liTimeout, &pctxtq->DpcExpireTimeSlice);
while ((plist = ListRemoveHead(&pctxtq->plistCtxtQ)) != NULL) { pctxt = CONTAINING_RECORD(plist, CTXT, listQueue);
ASSERT(pctxt->pplistCtxtQueue == &pctxtq->plistCtxtQ);
pctxt->pplistCtxtQueue = NULL; pctxt->dwfCtxt &= ~CTXTF_IN_READYQ; RunContext(pctxt); }
if (pctxtq->plistCtxtQ == NULL) { KeCancelTimer(&pctxtq->Timer); pctxtq->dwfCtxtQ &= ~CQF_TIMESLICE_EXPIRED; } else if (!(pctxtq->dwfCtxtQ & CQF_WORKITEM_SCHEDULED)) { //
// Our time slice has expired, reschedule another time slice if not
// already done so.
//
liTimeout.QuadPart = (INT_PTR)(-10000*(INT_PTR)pctxtq->dwmsTimeSliceInterval); KeSetTimer(&pctxtq->Timer, liTimeout, &pctxtq->DpcStartTimeSlice); }
EXIT(2, ("DispatchCtxtQueue!\n")); } //DispatchCtxtQueue
/***LP InsertReadyQueue - Insert the context into the ready queue
* * ENTRY * pctxt -> CTXT * fDelayExecute - queue the request, don't execute now * * EXIT-SUCCESS * returns STATUS_SUCCESS * EXIT-FAILURE * returns AMLIERR_ code * * NOTE * The caller must acquire the CtxtQ mutex before entering this * routine and release it after exiting this routine. */
NTSTATUS LOCAL InsertReadyQueue(PCTXT pctxt, BOOLEAN fDelayExecute) { TRACENAME("INSERTREADYQUEUE") NTSTATUS rc = STATUS_SUCCESS;
ENTER(2, ("InsertReadyQueue(pctxt=%x,fDelayExecute=%x)\n", pctxt, fDelayExecute));
CHKDEBUGGERREQ();
//
// Make sure we do have the spin lock.
//
LOGSCHEDEVENT('INSQ', (ULONG_PTR)pctxt, (ULONG_PTR) (pctxt->pnctxt? pctxt->pnctxt->pnsObj: pctxt->pnsObj), (ULONG_PTR)pctxt->pbOp); //
// If there is a pending timer, cancel it.
//
if (pctxt->dwfCtxt & CTXTF_TIMER_PENDING) { BOOLEAN fTimerCancelled;
pctxt->dwfCtxt &= ~CTXTF_TIMER_PENDING; fTimerCancelled = KeCancelTimer(&pctxt->Timer);
//
// If the timer could not be cancelled (already queued), wait
// for it to fire and dispatch the context from there. The
// pending timer is referring to this context and we can not
// have it completed with the timer outstanding. Plus this
// also interlocked to setting of timers and timeout processing
// to ensure that a timeout is not mistakenly performed on
// the next timer.
//
if (!fTimerCancelled) { pctxt->dwfCtxt |= CTXTF_TIMER_DISPATCH; } } //
// Make this context ready.
//
pctxt->dwfCtxt |= CTXTF_READY;
//
// If this context is already running, we are done; otherwise, process it.
//
if (!(pctxt->dwfCtxt & CTXTF_TIMER_DISPATCH) && (!(pctxt->dwfCtxt & CTXTF_RUNNING) || (pctxt->dwfCtxt & CTXTF_NEST_EVAL))) { if (fDelayExecute) { //
// This context is from a completion callback of current context,
// we need to unblock/restart current context.
//
ReleaseMutex(&gReadyQueue.mutCtxtQ); AsyncCallBack(pctxt, AMLISTA_CONTINUE); AcquireMutex(&gReadyQueue.mutCtxtQ); } else if ((pctxt->dwfCtxt & CTXTF_NEST_EVAL) && (gReadyQueue.pkthCurrent == KeGetCurrentThread())) { LOGSCHEDEVENT('NEST', (ULONG_PTR)pctxt, (ULONG_PTR) (pctxt->pnctxt? pctxt->pnctxt->pnsObj: pctxt->pnsObj), (ULONG_PTR)pctxt->pbOp); //
// Somebody is running a new method on the callout of the current
// context. We must run this new context first or else we will
// dead lock the current context. We assume that if pending is
// returned, the callout will return.
//
rc = RunContext(pctxt); } else if ((gReadyQueue.pkthCurrent == NULL) && !(gReadyQueue.dwfCtxtQ & CQF_PAUSED)) //
// We only execute the method if we are not in paused state.
//
{ LOGSCHEDEVENT('EVAL', (ULONG_PTR)pctxt, (ULONG_PTR) (pctxt->pnctxt? pctxt->pnctxt->pnsObj: pctxt->pnsObj), (ULONG_PTR)pctxt->pbOp); //
// There is no active context and we can execute it immediately.
//
rc = RunContext(pctxt);
if ((gReadyQueue.plistCtxtQ != NULL) && !(gReadyQueue.dwfCtxtQ & CQF_WORKITEM_SCHEDULED)) { //
// If we have more jobs in the queue and we haven't scheduled
// a dispatch, schedule one.
//
LOGSCHEDEVENT('KICK', (ULONG_PTR)rc, 0, 0); OSQueueWorkItem(&gReadyQueue.WorkItem); gReadyQueue.dwfCtxtQ |= CQF_WORKITEM_SCHEDULED; } } else { //
// Insert the context in the ready queue.
//
ASSERT(!(pctxt->dwfCtxt & (CTXTF_IN_READYQ | CTXTF_RUNNING))); LOGSCHEDEVENT('QCTX', (ULONG_PTR)pctxt, (ULONG_PTR) (pctxt->pnctxt? pctxt->pnctxt->pnsObj: pctxt->pnsObj), (ULONG_PTR)pctxt->pbOp); if (!(pctxt->dwfCtxt & CTXTF_IN_READYQ)) { pctxt->dwfCtxt |= CTXTF_IN_READYQ; ListInsertTail(&pctxt->listQueue, &gReadyQueue.plistCtxtQ); pctxt->pplistCtxtQueue = &gReadyQueue.plistCtxtQ; }
pctxt->dwfCtxt |= CTXTF_NEED_CALLBACK; rc = AMLISTA_PENDING; } }
EXIT(2, ("InsertReadyQueue=%x\n", rc)); return rc; } //InsertReadyQueue
/***LP RestartContext - Restart a context
* * ENTRY * pctxt -> CTXT structure * fDelayExecute - TRUE to queue for delay execution * * EXIT-SUCCESS * returns STATUS_SUCCESS * EXIT-FAILURE * returns AMLIERR_ code * None */
NTSTATUS LOCAL RestartContext(PCTXT pctxt, BOOLEAN fDelayExecute) { TRACENAME("RESTARTCONTEXT") NTSTATUS rc = STATUS_SUCCESS; PRESTART prest;
ENTER(2, ("RestartContext(pctxt=%x,fDelayExecute=%x)\n", pctxt, fDelayExecute));
ASSERT(!(pctxt->dwfCtxt & CTXTF_TIMER_PENDING)); ASSERT((fDelayExecute == FALSE) || !(pctxt->dwfCtxt & CTXTF_ASYNC_EVAL));
LOGSCHEDEVENT('REST', (ULONG_PTR)pctxt, (ULONG_PTR) (pctxt->pnctxt? pctxt->pnctxt->pnsObj: pctxt->pnsObj), (ULONG_PTR)pctxt->pbOp); if (KeGetCurrentIrql() < DISPATCH_LEVEL) { AcquireMutex(&gReadyQueue.mutCtxtQ); rc = InsertReadyQueue(pctxt, fDelayExecute); ReleaseMutex(&gReadyQueue.mutCtxtQ); } else if ((prest = NEWRESTOBJ(sizeof(RESTART))) != NULL) { pctxt->dwfCtxt |= CTXTF_NEED_CALLBACK; prest->pctxt = pctxt; ExInitializeWorkItem(&prest->WorkItem, RestartCtxtPassive, prest); OSQueueWorkItem(&prest->WorkItem); rc = AMLISTA_PENDING; } else { rc = AMLI_LOGERR(AMLIERR_FATAL, ("RestartContext: failed to allocate restart context item")); }
EXIT(2, ("RestartContext=%x\n", rc)); return rc; } //RestartContext
/***LP RestartCtxtPassive - Restart context running at PASSIVE_LEVEL
* * ENTRY * prest-> RESTART * * EXIT * None */
VOID RestartCtxtPassive(PRESTART prest) { TRACENAME("RESTARTCTXTPASSIVE")
ENTER(2, ("RestartCtxtPassive(prest=%x)\n", prest));
AcquireMutex(&gReadyQueue.mutCtxtQ); InsertReadyQueue(prest->pctxt, (BOOLEAN)((prest->pctxt->dwfCtxt & CTXTF_ASYNC_EVAL) == 0)); ReleaseMutex(&gReadyQueue.mutCtxtQ);
FREERESTOBJ(prest);
EXIT(2, ("RestartCtxtPassive!\n")); } //RestartCtxtPassive
/***LP RestartCtxtCallback - Callback to restart a context
* * ENTRY * pctxtdata -> CTXTDATA structure * * EXIT * None */
VOID EXPORT RestartCtxtCallback(PCTXTDATA pctxtdata) { TRACENAME("RESTARTCTXTCALLBACK") PCTXT pctxt = CONTAINING_RECORD(pctxtdata, CTXT, CtxtData);
ENTER(2, ("RestartCtxtCallback(pctxt=%x)\n", pctxt));
ASSERT(pctxt->dwSig == SIG_CTXT); LOGSCHEDEVENT('RSCB', (ULONG_PTR)pctxt, 0, 0); RestartContext(pctxt, (BOOLEAN)((pctxt->dwfCtxt & CTXTF_ASYNC_EVAL) == 0));
EXIT(2, ("RestartCtxtCallback!\n")); } //RestartCtxtCallback
|