-/* $Id$
- *
- * COPYRIGHT: See COPYING in the top level directory
- * PROJECT: ReactOS kernel
+/*
+ * PROJECT: ReactOS Kernel
+ * LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ke/dpc.c
- * PURPOSE: Handle DPCs (Delayed Procedure Calls)
- *
- * PROGRAMMERS: David Welch (welch@mcmail.com)
+ * PURPOSE: Routines for CPU-level support
+ * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Philip Susi (phreak@iag.net)
* Eric Kohl (ekohl@abo.rhein-zeitung.de)
- * Alex Ionescu (alex@relsoft.net)
*/
-/*
- * NOTE: See also the higher level support routines in ntoskrnl/io/dpc.c
- */
+/* INCLUDES ******************************************************************/
-/* INCLUDES ***************************************************************/
+#define NTDDI_VERSION NTDDI_WS03
#include <ntoskrnl.h>
#define NDEBUG
-#include <internal/debug.h>
+#include <debug.h>
+
+/* GLOBALS *******************************************************************/
+
+ULONG KiMaximumDpcQueueDepth = 4;
+ULONG KiMinimumDpcRate = 3;
+ULONG KiAdjustDpcThreshold = 20;
+ULONG KiIdealDpcRate = 20;
+BOOLEAN KeThreadDpcEnable;
+KMUTEX KiGenericCallDpcMutex;
+
+/* PRIVATE FUNCTIONS *********************************************************/
+
+//
+// This routine executes at the end of a thread's quantum.
+// If the thread's quantum has expired, then a new thread is attempted
+// to be scheduled.
+//
+// If no candidate thread has been found, the routine will return, otherwise
+// it will swap contexts to the next scheduled thread.
+//
+VOID
+NTAPI
+KiQuantumEnd(VOID)
+{
+ PKPRCB Prcb = KeGetCurrentPrcb();
+ PKTHREAD NextThread, Thread = Prcb->CurrentThread;
+
+ /* Check if a DPC Event was requested to be signaled */
+ if (InterlockedExchange(&Prcb->DpcSetEventRequest, 0))
+ {
+ /* Signal it */
+ KeSetEvent(&Prcb->DpcEvent, 0, 0);
+ }
-/* TYPES *******************************************************************/
+ /* Raise to synchronization level and lock the PRCB and thread */
+ KeRaiseIrqlToSynchLevel();
+ KiAcquireThreadLock(Thread);
+ KiAcquirePrcbLock(Prcb);
-#define MAX_QUANTUM 0x7F
+ /* Check if Quantum expired */
+ if (Thread->Quantum <= 0)
+ {
+ /* Make sure that we're not real-time or without a quantum */
+ if ((Thread->Priority < LOW_REALTIME_PRIORITY) &&
+ !(Thread->ApcState.Process->DisableQuantum))
+ {
+ /* Reset the new Quantum */
+ Thread->Quantum = Thread->QuantumReset;
+
+ /* Calculate new priority */
+ Thread->Priority = KiComputeNewPriority(Thread, 1);
+
+ /* Check if a new thread is scheduled */
+ if (!Prcb->NextThread)
+ {
+#ifdef NEW_SCHEDULER
+ /* Get a new ready thread */
+ NextThread = KiSelectReadyThread(Thread->Priority, Prcb);
+ if (NextThread)
+ {
+ /* Found one, set it on standby */
+ NextThread->Standby;
+ Prcb->NextThread = NewThread;
+ }
+#else
+ /* Just leave now */
+ KiReleasePrcbLock(Prcb);
+ KeLowerIrql(DISPATCH_LEVEL);
+ KiDispatchThread(Ready);
+ return;
+#endif
+ }
+ else
+ {
+ /* Otherwise, make sure that this thread doesn't get preempted */
+ Thread->Preempted = FALSE;
+ }
+ }
+ else
+ {
+ /* Otherwise, set maximum quantum */
+ Thread->Quantum = MAX_QUANTUM;
+ }
+ }
-/* FUNCTIONS ****************************************************************/
+ /* Release the thread lock */
+ KiReleaseThreadLock(Thread);
+
+ /* Check if there's no thread scheduled */
+ if (!Prcb->NextThread)
+ {
+ /* Just leave now */
+ KiReleasePrcbLock(Prcb);
+ KeLowerIrql(DISPATCH_LEVEL);
+ return;
+ }
+
+ /* Get the next thread now */
+ NextThread = Prcb->NextThread;
+
+ /* Set current thread's swap busy to true */
+ KiSetThreadSwapBusy(Thread);
+
+ /* Switch threads in PRCB */
+ Prcb->NextThread = NULL;
+ Prcb->CurrentThread = NextThread;
+
+ /* Set thread to running and the switch reason to Quantum End */
+ NextThread->State = Running;
+ Thread->WaitReason = WrQuantumEnd;
+
+ /* Queue it on the ready lists */
+ KxQueueReadyThread(Thread, Prcb);
+
+ /* Set wait IRQL to APC_LEVEL */
+ Thread->WaitIrql = APC_LEVEL;
+
+ /* Swap threads */
+ KiSwapContext(Thread, NextThread);
+
+ /* Lower IRQL back to DISPATCH_LEVEL */
+ KeLowerIrql(DISPATCH_LEVEL);
+}
-/*
- * FUNCTION: Initialize DPC handling
- */
VOID
-INIT_FUNCTION
-KeInitDpc(PKPRCB Prcb)
+FASTCALL
+KiRetireDpcList(IN PKPRCB Prcb)
{
- InitializeListHead(&Prcb->DpcData[0].DpcListHead);
-#if 0
- /*
- * FIXME:
- * Prcb->DpcEvent is a NULL pointer.
- */
- KeInitializeEvent(Prcb->DpcEvent, 0, 0);
-#endif
- KeInitializeSpinLock(&Prcb->DpcData[0].DpcLock);
- Prcb->MaximumDpcQueueDepth = 4;
- Prcb->MinimumDpcRate = 3;
- Prcb->DpcData[0].DpcQueueDepth = 0;
+ PKDPC_DATA DpcData = Prcb->DpcData;
+ PLIST_ENTRY DpcEntry;
+ PKDPC Dpc;
+ PKDEFERRED_ROUTINE DeferredRoutine;
+ PVOID DeferredContext, SystemArgument1, SystemArgument2;
+
+ /* Main outer loop */
+ do
+ {
+ /* Set us as active */
+ Prcb->DpcRoutineActive = TRUE;
+
+ /* Check if this is a timer expiration request */
+ if (Prcb->TimerRequest)
+ {
+ /* FIXME: Not yet implemented */
+ ASSERT(FALSE);
+ }
+
+ /* Loop while we have entries in the queue */
+ while (DpcData->DpcQueueDepth)
+ {
+ /* Lock the DPC data */
+ KefAcquireSpinLockAtDpcLevel(&DpcData->DpcLock);
+
+ /* Make sure we have an entry */
+ if (!IsListEmpty(&DpcData->DpcListHead))
+ {
+ /* Remove the DPC from the list */
+ DpcEntry = RemoveHeadList(&DpcData->DpcListHead);
+ Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry);
+
+ /* Clear its DPC data and save its parameters */
+ Dpc->DpcData = NULL;
+ DeferredRoutine = Dpc->DeferredRoutine;
+ DeferredContext = Dpc->DeferredContext;
+ SystemArgument1 = Dpc->SystemArgument1;
+ SystemArgument2 = Dpc->SystemArgument2;
+
+ /* Decrease the queue depth */
+ DpcData->DpcQueueDepth--;
+
+ /* Clear DPC Time */
+ Prcb->DebugDpcTime = 0;
+
+ /* Release the lock */
+ KefReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
+
+ /* Re-enable interrupts */
+ _enable();
+
+ /* Call the DPC */
+ DeferredRoutine(Dpc,
+ DeferredContext,
+ SystemArgument1,
+ SystemArgument2);
+ ASSERT_IRQL(DISPATCH_LEVEL);
+
+ /* Disable interrupts and keep looping */
+ _disable();
+ }
+ else
+ {
+ /* The queue should be flushed now */
+ ASSERT(DpcData->DpcQueueDepth == 0);
+
+ /* Release DPC Lock */
+ KefReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
+ break;
+ }
+ }
+
+ /* Clear DPC Flags */
+ Prcb->DpcRoutineActive = FALSE;
+ Prcb->DpcInterruptRequested = FALSE;
+
+ /* Check if we have deferred threads */
+ if (Prcb->DeferredReadyListHead.Next)
+ {
+ /* FIXME: 2K3-style scheduling not implemeted */
+ ASSERT(FALSE);
+ }
+ } while (DpcData->DpcQueueDepth);
}
-/*
- * @implemented
- */
VOID
-STDCALL
-KeInitializeThreadedDpc(PKDPC Dpc,
- PKDEFERRED_ROUTINE DeferredRoutine,
- PVOID DeferredContext)
-/*
- * FUNCTION:
- * Initalizes a Threaded DPC and registers the DeferredRoutine for it.
- * ARGUMENTS:
- * Dpc = Pointer to a caller supplied DPC to be initialized. The caller must allocate this memory.
- * DeferredRoutine = Pointer to associated DPC callback routine.
- * DeferredContext = Parameter to be passed to the callback routine.
- * NOTE: Callers can be running at any IRQL.
- */
+NTAPI
+KiInitializeDpc(IN PKDPC Dpc,
+ IN PKDEFERRED_ROUTINE DeferredRoutine,
+ IN PVOID DeferredContext,
+ IN KOBJECTS Type)
{
- DPRINT("Threaded DPC Initializing: %x with Routine: %x\n", Dpc, DeferredRoutine);
- Dpc->Type = ThreadedDpcObject;
+ /* Setup the DPC Object */
+ Dpc->Type = Type;
Dpc->Number= 0;
Dpc->Importance= MediumImportance;
Dpc->DeferredRoutine = DeferredRoutine;
Dpc->DpcData = NULL;
}
+/* PUBLIC FUNCTIONS **********************************************************/
+
/*
* @implemented
- *
- * FUNCTION:
- * Initalizes a DPC and registers the DeferredRoutine for it.
- * ARGUMENTS:
- * Dpc = Pointer to a caller supplied DPC to be initialized. The caller must allocate this memory.
- * DeferredRoutine = Pointer to associated DPC callback routine.
- * DeferredContext = Parameter to be passed to the callback routine.
- * NOTE: Callers can be running at any IRQL.
*/
VOID
-STDCALL
-KeInitializeDpc(PKDPC Dpc,
- PKDEFERRED_ROUTINE DeferredRoutine,
- PVOID DeferredContext)
+NTAPI
+KeInitializeThreadedDpc(IN PKDPC Dpc,
+ IN PKDEFERRED_ROUTINE DeferredRoutine,
+ IN PVOID DeferredContext)
{
- DPRINT("DPC Initializing: %x with Routine: %x\n", Dpc, DeferredRoutine);
- Dpc->Type = DpcObject;
- Dpc->Number= 0;
- Dpc->Importance= MediumImportance;
- Dpc->DeferredRoutine = DeferredRoutine;
- Dpc->DeferredContext = DeferredContext;
- Dpc->DpcData = NULL;
+ /* Call the internal routine */
+ KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, ThreadedDpcObject);
}
/*
* @implemented
- *
- * FUNCTION:
- * Queues a DPC for execution when the IRQL of a processor
- * drops below DISPATCH_LEVEL
- * ARGUMENTS:
- * Dpc = Pointed to a DPC Object Initalized by KeInitializeDpc.
- * SystemArgument1 = Driver Determined context data
- * SystemArgument2 = Driver Determined context data
- * RETURNS:
- * TRUE if the DPC object wasn't already in the queue
- * FALSE otherwise
- * NOTES:
- * If there is currently a DPC active on the target processor, or a DPC
- * interrupt has already been requested on the target processor when a
- * DPC is queued, then no further action is necessary. The DPC will be
- * executed on the target processor when its queue entry is processed.
- *
- * If there is not a DPC active on the target processor and a DPC interrupt
- * has not been requested on the target processor, then the exact treatment
- * of the DPC is dependent on whether the host system is a UP system or an
- * MP system.
- *
- * UP system.
- * ----------
- * If the DPC is of medium or high importance, the current DPC queue depth
- * is greater than the maximum target depth, or current DPC request rate is
- * less the minimum target rate, then a DPC interrupt is requested on the
- * host processor and the DPC will be processed when the interrupt occurs.
- * Otherwise, no DPC interupt is requested and the DPC execution will be
- * delayed until the DPC queue depth is greater that the target depth or the
- * minimum DPC rate is less than the target rate.
- *
- * MP system.
- * ----------
- * If the DPC is being queued to another processor and the depth of the DPC
- * queue on the target processor is greater than the maximum target depth or
- * the DPC is of high importance, then a DPC interrupt is requested on the
- * target processor and the DPC will be processed when the interrupt occurs.
- * Otherwise, the DPC execution will be delayed on the target processor until
- * the DPC queue depth on the target processor is greater that the maximum
- * target depth or the minimum DPC rate on the target processor is less than
- * the target mimimum rate.
- *
- * If the DPC is being queued to the current processor and the DPC is not of
- * low importance, the current DPC queue depth is greater than the maximum
- * target depth, or the minimum DPC rate is less than the minimum target rate,
- * then a DPC interrupt is request on the current processor and the DPV will
- * be processed whne the interrupt occurs. Otherwise, no DPC interupt is
- * requested and the DPC execution will be delayed until the DPC queue depth
- * is greater that the target depth or the minimum DPC rate is less than the
- * target rate.
*/
-BOOLEAN
-STDCALL
-KeInsertQueueDpc(PKDPC Dpc,
- PVOID SystemArgument1,
- PVOID SystemArgument2)
+VOID
+NTAPI
+KeInitializeDpc(IN PKDPC Dpc,
+ IN PKDEFERRED_ROUTINE DeferredRoutine,
+ IN PVOID DeferredContext)
{
- KIRQL OldIrql;
- PKPRCB Prcb;
+ /* Call the internal routine */
+ KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, DpcObject);
+}
- DPRINT("KeInsertQueueDpc(DPC %x, SystemArgument1 %x, SystemArgument2 %x)\n",
- Dpc, SystemArgument1, SystemArgument2);
+/*
+ * @implemented
+ */
+BOOLEAN
+NTAPI
+KeInsertQueueDpc(IN PKDPC Dpc,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2)
+{
+ KIRQL OldIrql;
+ PKPRCB Prcb, CurrentPrcb = KeGetCurrentPrcb();
+ ULONG Cpu;
+ PKDPC_DATA DpcData;
+ BOOLEAN DpcConfigured = FALSE, DpcInserted = FALSE;
+ ASSERT_DPC(Dpc);
/* Check IRQL and Raise it to HIGH_LEVEL */
- ASSERT(KeGetCurrentIrql()>=DISPATCH_LEVEL);
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
-
- /* Check if this is a Thread DPC, which we don't support (yet) */
- if (Dpc->Type == ThreadedDpcObject) {
- return FALSE;
- KeLowerIrql(OldIrql);
- }
-#ifdef CONFIG_SMP
- /* Get the right PCR for this CPU */
- if (Dpc->Number >= MAXIMUM_PROCESSORS) {
-
- ASSERT (Dpc->Number - MAXIMUM_PROCESSORS < KeNumberProcessors);
- Prcb = ((PKPCR)((ULONG_PTR)KPCR_BASE + ((Dpc->Number - MAXIMUM_PROCESSORS) * PAGE_SIZE)))->Prcb;
-
- } else {
-
- ASSERT (Dpc->Number < KeNumberProcessors);
- Prcb = KeGetCurrentPrcb();
- Dpc->Number = KeGetCurrentProcessorNumber();
+ /* Check if the DPC has more then the maximum number of CPUs */
+ if (Dpc->Number >= MAXIMUM_PROCESSORS)
+ {
+ /* Then substract the maximum and get that PRCB. */
+ Cpu = Dpc->Number - MAXIMUM_PROCESSORS;
+ Prcb = KiProcessorBlock[Cpu];
+ }
+ else
+ {
+ /* Use the current one */
+ Prcb = CurrentPrcb;
+ Cpu = Prcb->Number;
}
-
- KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
-#else
- Prcb = ((PKPCR)KPCR_BASE)->Prcb;
-#endif
- /* Get the DPC Data */
- if (InterlockedCompareExchangeUL(&Dpc->DpcData, &Prcb->DpcData[0].DpcLock, 0)) {
-
- DPRINT("DPC Already Inserted");
-#ifdef CONFIG_SMP
- KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
-#endif
- KeLowerIrql(OldIrql);
- return(FALSE);
+ /* Check if this is a threaded DPC and threaded DPCs are enabled */
+ if ((Dpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))
+ {
+ /* Then use the threaded data */
+ DpcData = &Prcb->DpcData[DPC_THREADED];
}
-
- /* Make sure the lists are free if the Queue is 0 */
- if (Prcb->DpcData[0].DpcQueueDepth == 0) {
-
- ASSERT(IsListEmpty(&Prcb->DpcData[0].DpcListHead));
- } else {
-
- ASSERT(!IsListEmpty(&Prcb->DpcData[0].DpcListHead));
+ else
+ {
+ /* Otherwise, use the regular data */
+ DpcData = &Prcb->DpcData[DPC_NORMAL];
}
- /* Now we can play with the DPC safely */
- Dpc->SystemArgument1=SystemArgument1;
- Dpc->SystemArgument2=SystemArgument2;
- Prcb->DpcData[0].DpcQueueDepth++;
- Prcb->DpcData[0].DpcCount++;
-
- /* Insert the DPC into the list. HighImportance DPCs go at the beginning */
- if (Dpc->Importance == HighImportance) {
-
- InsertHeadList(&Prcb->DpcData[0].DpcListHead, &Dpc->DpcListEntry);
- } else {
-
- InsertTailList(&Prcb->DpcData[0].DpcListHead, &Dpc->DpcListEntry);
- }
- DPRINT("New DPC Added. Dpc->DpcListEntry.Flink %x\n", Dpc->DpcListEntry.Flink);
-
- /* Make sure a DPC isn't executing already and respect rules outlined above. */
- if ((!Prcb->DpcRoutineActive) && (!Prcb->DpcInterruptRequested)) {
-
-#ifdef CONFIG_SMP
- /* Check if this is the same CPU */
- if (Prcb != KeGetCurrentPrcb()) {
-
- /* Send IPI if High Importance */
- if ((Dpc->Importance == HighImportance) ||
- (Prcb->DpcData[0].DpcQueueDepth >= Prcb->MaximumDpcQueueDepth)) {
-
- if (Dpc->Number >= MAXIMUM_PROCESSORS) {
-
- KiIpiSendRequest(1 << (Dpc->Number - MAXIMUM_PROCESSORS), IPI_REQUEST_DPC);
- } else {
-
- KiIpiSendRequest(1 << Dpc->Number, IPI_REQUEST_DPC);
- }
+ /* Acquire the DPC lock */
+ KiAcquireSpinLock(&DpcData->DpcLock);
+ /* Get the DPC Data */
+ if (!InterlockedCompareExchangePointer(&Dpc->DpcData, DpcData, NULL))
+ {
+ /* Now we can play with the DPC safely */
+ Dpc->SystemArgument1 = SystemArgument1;
+ Dpc->SystemArgument2 = SystemArgument2;
+ DpcData->DpcQueueDepth++;
+ DpcData->DpcCount++;
+ DpcConfigured = TRUE;
+
+ /* Check if this is a high importance DPC */
+ if (Dpc->Importance == HighImportance)
+ {
+ /* Pre-empty other DPCs */
+ InsertHeadList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
+ }
+ else
+ {
+ /* Add it at the end */
+ InsertTailList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
+ }
+
+ /* Check if this is the DPC on the threaded list */
+ if (&Prcb->DpcData[DPC_THREADED].DpcListHead == &DpcData->DpcListHead)
+ {
+ /* Make sure a threaded DPC isn't already active */
+ if (!(Prcb->DpcThreadActive) && !(Prcb->DpcThreadRequested))
+ {
+ /* FIXME: Setup Threaded DPC */
+ ASSERT(FALSE);
}
- } else {
-
- /* Request an Interrupt only if the DPC isn't low priority */
- if ((Dpc->Importance != LowImportance) ||
- (Prcb->DpcData[0].DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) ||
- (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) {
-
- /* Request Interrupt */
- HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
- Prcb->DpcInterruptRequested = TRUE;
+ }
+ else
+ {
+ /* Make sure a DPC isn't executing already */
+ if (!(Prcb->DpcRoutineActive) && !(Prcb->DpcInterruptRequested))
+ {
+ /* Check if this is the same CPU */
+ if (Prcb != CurrentPrcb)
+ {
+ /*
+ * Check if the DPC is of high importance or above the
+ * maximum depth. If it is, then make sure that the CPU
+ * isn't idle, or that it's sleeping.
+ */
+ if (((Dpc->Importance == HighImportance) ||
+ (DpcData->DpcQueueDepth >=
+ Prcb->MaximumDpcQueueDepth)) &&
+ (!(AFFINITY_MASK(Cpu) & KiIdleSummary) ||
+ (Prcb->Sleeping)))
+ {
+ /* Set interrupt requested */
+ Prcb->DpcInterruptRequested = TRUE;
+
+ /* Set DPC inserted */
+ DpcInserted = TRUE;
+ }
+ }
+ else
+ {
+ /* Check if the DPC is of anything but low importance */
+ if ((Dpc->Importance != LowImportance) ||
+ (DpcData->DpcQueueDepth >=
+ Prcb->MaximumDpcQueueDepth) ||
+ (Prcb->DpcRequestRate < Prcb->MinimumDpcRate))
+ {
+ /* Set interrupt requested */
+ Prcb->DpcInterruptRequested = TRUE;
+
+ /* Set DPC inserted */
+ DpcInserted = TRUE;
+ }
+ }
}
}
-#else
- DPRINT("Requesting Interrupt. Importance: %x. QueueDepth: %x. MaxQueue: %x . RequestRate: %x. MinRate:%x \n", Dpc->Importance, Prcb->DpcData[0].DpcQueueDepth, Prcb->MaximumDpcQueueDepth, Prcb->DpcRequestRate, Prcb->MinimumDpcRate);
-
- /* Request an Interrupt only if the DPC isn't low priority */
- if ((Dpc->Importance != LowImportance) ||
- (Prcb->DpcData[0].DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) ||
- (Prcb->DpcRequestRate < Prcb->MinimumDpcRate)) {
-
- /* Request Interrupt */
- DPRINT("Requesting Interrupt\n");
+ }
+
+ /* Release the lock */
+ KiReleaseSpinLock(&DpcData->DpcLock);
+
+ /* Check if the DPC was inserted */
+ if (DpcInserted)
+ {
+ /* Check if this was SMP */
+ if (Prcb != CurrentPrcb)
+ {
+ /* It was, request and IPI */
+ KiIpiSendRequest(AFFINITY_MASK(Cpu), IPI_DPC);
+ }
+ else
+ {
+ /* It wasn't, request an interrupt from HAL */
HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
- Prcb->DpcInterruptRequested = TRUE;
}
-#endif
}
-#ifdef CONFIG_SMP
- KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
-#endif
- /* Lower IRQL */
+
+ /* Lower IRQL */
KeLowerIrql(OldIrql);
- return(TRUE);
+ return DpcConfigured;
}
/*
* @implemented
- *
- * FUNCTION:
- * Removes DPC object from the system dpc queue
- * ARGUMENTS:
- * Dpc = Pointer to DPC to remove from the queue.
- * RETURNS:
- * TRUE if the DPC was in the queue
- * FALSE otherwise
*/
-BOOLEAN
-STDCALL
-KeRemoveQueueDpc(PKDPC Dpc)
+BOOLEAN
+NTAPI
+KeRemoveQueueDpc(IN PKDPC Dpc)
{
- BOOLEAN WasInQueue;
- KIRQL OldIrql;
-
- /* Raise IRQL */
- DPRINT("Removing DPC: %x\n", Dpc);
- KeRaiseIrql(HIGH_LEVEL, &OldIrql);
-#ifdef CONFIG_SMP
- KiAcquireSpinLock(&((PKDPC_DATA)Dpc->DpcData)->DpcLock);
-#endif
-
- /* First make sure the DPC lock isn't being held */
- WasInQueue = Dpc->DpcData ? TRUE : FALSE;
- if (Dpc->DpcData) {
-
- /* Remove the DPC */
- ((PKDPC_DATA)Dpc->DpcData)->DpcQueueDepth--;
- RemoveEntryList(&Dpc->DpcListEntry);
+ PKDPC_DATA DpcData;
+ UCHAR DpcType;
+ ASSERT_DPC(Dpc);
+
+ /* Disable interrupts */
+ _disable();
+
+ /* Get DPC data and type */
+ DpcType = Dpc->Type;
+ DpcData = Dpc->DpcData;
+ if (DpcData)
+ {
+ /* Acquire the DPC lock */
+ KiAcquireSpinLock(&DpcData->DpcLock);
+
+ /* Make sure that the data didn't change */
+ if (DpcData == Dpc->DpcData)
+ {
+ /* Remove the DPC */
+ DpcData->DpcQueueDepth--;
+ RemoveEntryList(&Dpc->DpcListEntry);
+ Dpc->DpcData = NULL;
+ }
+ /* Release the lock */
+ KiReleaseSpinLock(&DpcData->DpcLock);
}
-#ifdef CONFIG_SMP
- KiReleaseSpinLock(&((PKDPC_DATA)Dpc->DpcData)->DpcLock);
-#endif
+
+ /* Re-enable interrupts */
+ _enable();
/* Return if the DPC was in the queue or not */
- KeLowerIrql(OldIrql);
- return WasInQueue;
+ return DpcData ? TRUE : FALSE;
}
/*
* @implemented
*/
VOID
-STDCALL
+NTAPI
KeFlushQueuedDpcs(VOID)
-/*
- * FUNCTION:
- * Called to Deliver DPCs if any are pending.
- * NOTES:
- * Called when deleting a Driver.
- */
{
- /* Request an interrupt if needed */
- if (KeGetCurrentPrcb()->DpcData[0].DpcQueueDepth) HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
+ PAGED_CODE();
+
+ /* Check if this is an UP machine */
+ if (KeActiveProcessors == 1)
+ {
+ /* Check if there are DPCs on either queues */
+ if ((KeGetCurrentPrcb()->DpcData[DPC_NORMAL].DpcQueueDepth) ||
+ (KeGetCurrentPrcb()->DpcData[DPC_THREADED].DpcQueueDepth))
+ {
+ /* Request an interrupt */
+ HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
+ }
+ }
+ else
+ {
+ /* FIXME: SMP support required */
+ ASSERT(FALSE);
+ }
}
/*
* @implemented
*/
-BOOLEAN
-STDCALL
-KeIsExecutingDpc(
- VOID
-)
+BOOLEAN
+NTAPI
+KeIsExecutingDpc(VOID)
{
/* Return if the Dpc Routine is active */
return KeGetCurrentPrcb()->DpcRoutineActive;
}
/*
- * FUNCTION: Specifies the DPCs importance
- * ARGUMENTS:
- * Dpc = Initalizes DPC
- * Importance = DPC importance
- * RETURNS: None
- *
* @implemented
*/
-VOID
-STDCALL
+VOID
+NTAPI
KeSetImportanceDpc (IN PKDPC Dpc,
- IN KDPC_IMPORTANCE Importance)
+ IN KDPC_IMPORTANCE Importance)
{
/* Set the DPC Importance */
+ ASSERT_DPC(Dpc);
Dpc->Importance = Importance;
}
/*
* @implemented
- *
- * FUNCTION: Specifies on which processor the DPC will run
- * ARGUMENTS:
- * Dpc = Initalizes DPC
- * Number = Processor number
- * RETURNS: None
*/
-VOID
-STDCALL
+VOID
+NTAPI
KeSetTargetProcessorDpc(IN PKDPC Dpc,
IN CCHAR Number)
{
- /* Check how many CPUs are on the system */
- if (Number >= MAXIMUM_PROCESSORS) {
-
- /* No CPU Number */
- Dpc->Number = 0;
-
- } else {
-
- /* Set the Number Specified */
- ASSERT(Number < KeNumberProcessors);
- Dpc->Number = Number + MAXIMUM_PROCESSORS;
- }
-}
-
-/*
- * FUNCTION:
- * Called when a quantum end occurs to check if priority should be changed
- * and wether a new thread should be dispatched.
- * NOTES:
- * Called when deleting a Driver.
- */
-VOID
-STDCALL
-KiQuantumEnd(VOID)
-{
- PKPRCB Prcb;
- PKTHREAD CurrentThread;
- KIRQL OldIrql;
- PKPROCESS Process;
- KPRIORITY OldPriority;
- KPRIORITY NewPriority;
-
- /* Lock dispatcher, get current thread */
- Prcb = KeGetCurrentPrcb();
- CurrentThread = KeGetCurrentThread();
- OldIrql = KeRaiseIrqlToSynchLevel();
-
- /* Get the Thread's Process */
- Process = CurrentThread->ApcState.Process;
-
- /* Set DPC Event if requested */
- if (Prcb->DpcSetEventRequest) {
- /*
- * FIXME:
- * Prcb->DpcEvent is not initialized.
- */
- KEBUGCHECK(0);
- KeSetEvent(Prcb->DpcEvent, 0, 0);
- }
-
- /* Check if Quantum expired */
- if (CurrentThread->Quantum <= 0) {
- /* Set the new Quantum */
- CurrentThread->Quantum = Process->ThreadQuantum;
-
- /* Calculate new priority */
- OldPriority = CurrentThread->Priority;
- if (OldPriority < LOW_REALTIME_PRIORITY) {
-
- /* Set the New Priority and add the Priority Decrement */
- NewPriority = OldPriority - CurrentThread->PriorityDecrement - 1;
-
- /* Don't go out of bounds */
- if (NewPriority < CurrentThread->BasePriority) NewPriority = CurrentThread->BasePriority;
-
- /* Reset the priority decrement */
- CurrentThread->PriorityDecrement = 0;
-
- /* Set a new priority if needed */
- if (OldPriority != NewPriority) {
-
- /* Set new Priority */
- CurrentThread->Priority = NewPriority;
-
- } else {
-
- /* Queue new thread if none is already */
- if (Prcb->NextThread == NULL) {
-
- /* FIXME: Schedule a New Thread, when ROS will have NT Scheduler */
-
- } else {
-
- /* Make the current thread non-premeptive if a new thread is queued */
- CurrentThread->Preempted = FALSE;
- }
- }
-
-
- } else {
- /* Set the Quantum back to Maximum */
- //if (CurrentThread->DisableQuantum) {
- // CurrentThread->Quantum = MAX_QUANTUM;
- //}
- }
- }
-
- /* Dispatch the Thread */
- KeLowerIrql(DISPATCH_LEVEL);
- KiDispatchThread(THREAD_STATE_READY);
-}
-
-/*
- * @implemented
- *
- * FUNCTION:
- * Called whenever a system interrupt is generated at DISPATCH_LEVEL.
- * It delivers queued DPCs and dispatches a new thread if need be.
- */
-VOID
-STDCALL
-KiDispatchInterrupt(VOID)
-{
- PLIST_ENTRY DpcEntry;
- PKDPC Dpc;
- KIRQL OldIrql;
- PKPRCB Prcb;
-
- DPRINT("Dispatching Interrupts\n");
- ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
-
- /* Set DPC Deliver to Active */
- Prcb = KeGetCurrentPrcb();
-
- if (Prcb->DpcData[0].DpcQueueDepth > 0) {
- /* Raise IRQL */
- KeRaiseIrql(HIGH_LEVEL, &OldIrql);
-#ifdef CONFIG_SMP
- KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
-#endif
- Prcb->DpcRoutineActive = TRUE;
-
- DPRINT("&Prcb->DpcData[0].DpcListHead: %x\n", &Prcb->DpcData[0].DpcListHead);
- /* Loop while we have entries */
- while (!IsListEmpty(&Prcb->DpcData[0].DpcListHead)) {
-
- ASSERT(Prcb->DpcData[0].DpcQueueDepth > 0);
- DPRINT("Queue Depth: %x\n", Prcb->DpcData[0].DpcQueueDepth);
-
- /* Get the DPC call it */
- DpcEntry = RemoveHeadList(&Prcb->DpcData[0].DpcListHead);
- Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry);
- DPRINT("Dpc->DpcListEntry.Flink %x\n", Dpc->DpcListEntry.Flink);
- Dpc->DpcData = NULL;
- Prcb->DpcData[0].DpcQueueDepth--;
-#ifdef CONFIG_SMP
- KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
-#endif
- /* Disable/Enabled Interrupts and Call the DPC */
- KeLowerIrql(OldIrql);
- DPRINT("Calling DPC: %x\n", Dpc);
- Dpc->DeferredRoutine(Dpc,
- Dpc->DeferredContext,
- Dpc->SystemArgument1,
- Dpc->SystemArgument2);
- KeRaiseIrql(HIGH_LEVEL, &OldIrql);
-
-#ifdef CONFIG_SMP
- KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
- /*
- * If the dpc routine drops the irql below DISPATCH_LEVEL,
- * a thread switch can occur and after the next thread switch
- * the execution may start on an other processor.
- */
- if (Prcb != KeGetCurrentPrcb()) {
-
- Prcb->DpcRoutineActive = FALSE;
- KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
- Prcb = KeGetCurrentPrcb();
- KiAcquireSpinLock(&Prcb->DpcData[0].DpcLock);
- Prcb->DpcRoutineActive = TRUE;
- }
-#endif
- }
- /* Clear DPC Flags */
- Prcb->DpcRoutineActive = FALSE;
- Prcb->DpcInterruptRequested = FALSE;
-#ifdef CONFIG_SMP
- KiReleaseSpinLock(&Prcb->DpcData[0].DpcLock);
-#endif
-
- /* DPC Dispatching Ended, re-enable interrupts */
- KeLowerIrql(OldIrql);
- }
-
- DPRINT("Checking for Quantum End\n");
-
- /* If we have Quantum End, call the function */
- if (Prcb->QuantumEnd) {
-
- Prcb->QuantumEnd = FALSE;
- KiQuantumEnd();
- }
+ /* Set a target CPU */
+ ASSERT_DPC(Dpc);
+ Dpc->Number = Number + MAXIMUM_PROCESSORS;
}
/* EOF */