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
413 lines
12 KiB
/*** 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
|