/*
- * 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)
- * PROGRAMMER: David Welch (welch@mcmail.com)
- * UPDATE HISTORY:
- * 28/05/98: Created
+ * 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)
*/
+/* INCLUDES ******************************************************************/
+
+#define NTDDI_VERSION NTDDI_WS03
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#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);
+ }
+
+ /* Raise to synchronization level and lock the PRCB and thread */
+ KeRaiseIrqlToSynchLevel();
+ KiAcquireThreadLock(Thread);
+ KiAcquirePrcbLock(Prcb);
+
+ /* 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;
+ }
+ }
+
+ /* 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);
+}
+
+VOID
+FASTCALL
+KiRetireDpcList(IN PKPRCB Prcb)
+{
+ 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);
+}
+
+VOID
+NTAPI
+KiInitializeDpc(IN PKDPC Dpc,
+ IN PKDEFERRED_ROUTINE DeferredRoutine,
+ IN PVOID DeferredContext,
+ IN KOBJECTS Type)
+{
+ /* Setup the DPC Object */
+ Dpc->Type = Type;
+ Dpc->Number= 0;
+ Dpc->Importance= MediumImportance;
+ Dpc->DeferredRoutine = DeferredRoutine;
+ Dpc->DeferredContext = DeferredContext;
+ Dpc->DpcData = NULL;
+}
+
+/* PUBLIC FUNCTIONS **********************************************************/
+
/*
- * NOTE: See also the higher level support routines in ntoskrnl/io/dpc.c
+ * @implemented
*/
+VOID
+NTAPI
+KeInitializeThreadedDpc(IN PKDPC Dpc,
+ IN PKDEFERRED_ROUTINE DeferredRoutine,
+ IN PVOID DeferredContext)
+{
+ /* Call the internal routine */
+ KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, ThreadedDpcObject);
+}
-/* INCLUDES ***************************************************************/
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+KeInitializeDpc(IN PKDPC Dpc,
+ IN PKDEFERRED_ROUTINE DeferredRoutine,
+ IN PVOID DeferredContext)
+{
+ /* Call the internal routine */
+ KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, DpcObject);
+}
-#include <ddk/ntddk.h>
+/*
+ * @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);
-#define NDEBUG
-#include <internal/debug.h>
+ /* Check IRQL and Raise it to HIGH_LEVEL */
+ KeRaiseIrql(HIGH_LEVEL, &OldIrql);
+
+ /* 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;
+ }
+
+ /* 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];
+ }
+ else
+ {
+ /* Otherwise, use the regular data */
+ DpcData = &Prcb->DpcData[DPC_NORMAL];
+ }
+
+ /* Acquire the DPC lock */
+ KiAcquireSpinLock(&DpcData->DpcLock);
-/* TYPES *******************************************************************/
+ /* 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;
-/* GLOBALS ******************************************************************/
+ /* 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);
+ }
-static LIST_ENTRY DpcQueueHead={NULL,NULL};
-static KSPIN_LOCK DpcQueueLock={0,};
-ULONG DpcQueueSize = 0;
+ /* 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
+ {
+ /* 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;
-/* FUNCTIONS ****************************************************************/
+ /* 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;
+ }
+ }
+ }
+ }
+ }
+
+ /* 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);
+ }
+ }
+
+ /* Lower IRQL */
+ KeLowerIrql(OldIrql);
+ return DpcConfigured;
+}
-VOID KeInitializeDpc(PKDPC Dpc, PKDEFERRED_ROUTINE DeferredRoutine,
- PVOID DeferredContext)
/*
- * FUNCTION: Initalizes a DPC
- * ARGUMENTS:
- * Dpc = Caller supplied DPC to be initialized
- * DeferredRoutine = Associated DPC callback
- * DeferredContext = Parameter to be passed to the callback
- * NOTE: Callers must be running at IRQL PASSIVE_LEVEL
+ * @implemented
*/
+BOOLEAN
+NTAPI
+KeRemoveQueueDpc(IN PKDPC Dpc)
{
- Dpc->Type=0;
- Dpc->DeferredRoutine=DeferredRoutine;
- Dpc->DeferredContext=DeferredContext;
- Dpc->Lock=0;
+ 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);
+ }
+
+ /* Re-enable interrupts */
+ _enable();
+
+ /* Return if the DPC was in the queue or not */
+ return DpcData ? TRUE : FALSE;
}
-void KeDrainDpcQueue(void)
/*
- * FUNCTION: Called to execute queued dpcs
+ * @implemented
*/
+VOID
+NTAPI
+KeFlushQueuedDpcs(VOID)
{
- PLIST_ENTRY current_entry;
- PKDPC current;
- KIRQL oldlvl;
-
- assert_irql(DISPATCH_LEVEL);
-
- if (DpcQueueSize == 0)
- {
- return;
- }
- DPRINT("KeDrainDpcQueue()\n");
-
- KeAcquireSpinLockAtDpcLevel(&DpcQueueLock);
- KeRaiseIrql(HIGH_LEVEL,&oldlvl);
- current_entry = RemoveHeadList(&DpcQueueHead);
- KeLowerIrql(oldlvl);
- current = CONTAINING_RECORD(current_entry,KDPC,DpcListEntry);
- while (current_entry!=(&DpcQueueHead))
- {
- CHECKPOINT;
- current->DeferredRoutine(current,current->DeferredContext,
- current->SystemArgument1,
- current->SystemArgument2);
- current->Lock=FALSE;
- KeRaiseIrql(HIGH_LEVEL,&oldlvl);
- current_entry = RemoveHeadList(&DpcQueueHead);
- DpcQueueSize--;
- KeLowerIrql(oldlvl);
- current = CONTAINING_RECORD(¤t_entry,KDPC,DpcListEntry);
- }
- KeReleaseSpinLockFromDpcLevel(&DpcQueueLock);
+ 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);
+ }
}
-BOOLEAN KeRemoveQueueDpc(PKDPC Dpc)
/*
- * FUNCTION: Removes DPC object from the system dpc queue
- * ARGUMENTS:
- * Dpc = DPC to remove
- * RETURNS: TRUE if the DPC was in the queue
- * FALSE otherwise
+ * @implemented
*/
+BOOLEAN
+NTAPI
+KeIsExecutingDpc(VOID)
{
- if (!Dpc->Lock)
- {
- return(FALSE);
- }
- RemoveEntryList(&Dpc->DpcListEntry);
- DpcQueueSize--;
- Dpc->Lock=0;
- return(TRUE);
+ /* Return if the Dpc Routine is active */
+ return KeGetCurrentPrcb()->DpcRoutineActive;
}
-BOOLEAN KeInsertQueueDpc(PKDPC dpc, PVOID SystemArgument1,
- PVOID SystemArgument2)
/*
- * FUNCTION: Queues a DPC for execution when the IRQL of a processor
- * drops below DISPATCH_LEVEL
- * ARGUMENTS:
- * Dpc = Initalizes DPC
- * SystemArguments[1-2] = Undocumented
- * RETURNS: TRUE if the DPC object wasn't already in the queue
- * FALSE otherwise
+ * @implemented
*/
+VOID
+NTAPI
+KeSetImportanceDpc (IN PKDPC Dpc,
+ IN KDPC_IMPORTANCE Importance)
{
- DPRINT("KeInsertQueueDpc(dpc %x, SystemArgument1 %x, SystemArgument2 %x)\n",
- dpc, SystemArgument1, SystemArgument2);
-
- assert(KeGetCurrentIrql()>=DISPATCH_LEVEL);
-
- dpc->Number=0;
- dpc->Importance=Medium;
- dpc->SystemArgument1=SystemArgument1;
- dpc->SystemArgument2=SystemArgument2;
- if (dpc->Lock)
- {
- return(FALSE);
- }
- KeAcquireSpinLockAtDpcLevel(&DpcQueueLock);
- InsertHeadList(&DpcQueueHead,&dpc->DpcListEntry);
- DpcQueueSize++;
- KeReleaseSpinLockFromDpcLevel(&DpcQueueLock);
- dpc->Lock=(PULONG)1;
- DPRINT("DpcQueueHead.Flink %x\n",DpcQueueHead.Flink);
- DPRINT("Leaving KeInsertQueueDpc()\n",0);
- return(TRUE);
+ /* Set the DPC Importance */
+ ASSERT_DPC(Dpc);
+ Dpc->Importance = Importance;
}
-void KeInitDpc(void)
/*
- * FUNCTION: Initialize DPC handling
+ * @implemented
*/
+VOID
+NTAPI
+KeSetTargetProcessorDpc(IN PKDPC Dpc,
+ IN CCHAR Number)
{
- InitializeListHead(&DpcQueueHead);
+ /* Set a target CPU */
+ ASSERT_DPC(Dpc);
+ Dpc->Number = Number + MAXIMUM_PROCESSORS;
}
+/* EOF */