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

/*** 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