- Simplify KiSelectReadyThread.
[reactos.git] / reactos / ntoskrnl / ke / dpc.c
index 0047b3a..2640ca0 100644 (file)
 /*
- *  ReactOS kernel
- *  Copyright (C) 2000, 1999, 1998 David Welch <welch@cwcom.net>,
- *                                 Philip Susi <phreak@iag.net>,
- *                                 Eric Kohl <ekohl@abo.rhein-zeitung.de>
- *                                 Alex Ionescu <alex@relsoft.net>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-/* $Id: dpc.c,v 1.46 2004/11/21 06:51:18 ion Exp $
- *
- * 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
- *                12/3/99:  Phillip Susi: Fixed IRQL problem
- *                12/11/04: Alex Ionescu - Major rewrite.
+ * 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)
  */
 
-/*
- * 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>
 
-/* TYPES *******************************************************************/
+/* GLOBALS *******************************************************************/
 
-#define MAX_QUANTUM 0x7F
-/* GLOBALS ******************************************************************/
+ULONG KiMaximumDpcQueueDepth = 4;
+ULONG KiMinimumDpcRate = 3;
+ULONG KiAdjustDpcThreshold = 20;
+ULONG KiIdealDpcRate = 20;
+BOOLEAN KeThreadDpcEnable;
+KMUTEX KiGenericCallDpcMutex;
 
-/* FUNCTIONS ****************************************************************/
+/* PRIVATE FUNCTIONS *********************************************************/
 
-VOID INIT_FUNCTION
-KeInitDpc(PKPCR Pcr)
-/*
- * FUNCTION: Initialize DPC handling
- */
+//
+// 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)
 {
-   InitializeListHead(&Pcr->PrcbData.DpcData[0].DpcListHead);
-   KeInitializeEvent(Pcr->PrcbData.DpcEvent, 0, 0);
-   KeInitializeSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock);
-   Pcr->PrcbData.MaximumDpcQueueDepth = 4;
-   Pcr->PrcbData.MinimumDpcRate = 3;
-   Pcr->PrcbData.DpcData[0].DpcQueueDepth = 0;    
+    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);
 }
 
-/*
- * @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.
- */
+FASTCALL
+KiRetireDpcList(IN PKPRCB Prcb)
 {
-       DPRINT("Threaded DPC Initializing: %x with Routine: %x\n", Dpc, DeferredRoutine);
-       //Dpc->Type = KThreadedDpc;
-       Dpc->Number= 0;
-       Dpc->Importance= MediumImportance;
-       Dpc->DeferredRoutine = DeferredRoutine;
-       Dpc->DeferredContext = DeferredContext;
-       Dpc->DpcData = NULL;
+    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
-KeInitializeDpc (PKDPC                 Dpc,
-                PKDEFERRED_ROUTINE     DeferredRoutine,
-                PVOID                  DeferredContext)
-/*
- * 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.
- */
+NTAPI
+KiInitializeDpc(IN PKDPC Dpc,
+                IN PKDEFERRED_ROUTINE DeferredRoutine,
+                IN PVOID DeferredContext,
+                IN KOBJECTS Type)
 {
-       DPRINT("DPC Initializing: %x with Routine: %x\n", Dpc, DeferredRoutine);
-       Dpc->Type = KDpc;
-       Dpc->Number= 0;
-       Dpc->Importance= MediumImportance;
-       Dpc->DeferredRoutine = DeferredRoutine;
-       Dpc->DeferredContext = DeferredContext;
-       Dpc->DpcData = NULL;
+    /* Setup the DPC Object */
+    Dpc->Type = Type;
+    Dpc->Number= 0;
+    Dpc->Importance= MediumImportance;
+    Dpc->DeferredRoutine = DeferredRoutine;
+    Dpc->DeferredContext = DeferredContext;
+    Dpc->DpcData = NULL;
 }
 
+/* PUBLIC FUNCTIONS **********************************************************/
+
 /*
  * @implemented
  */
-BOOLEAN STDCALL
-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 = 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.
- */
+VOID
+NTAPI
+KeInitializeThreadedDpc(IN PKDPC Dpc,
+                        IN PKDEFERRED_ROUTINE DeferredRoutine,
+                        IN PVOID DeferredContext)
 {
-       KIRQL OldIrql;
-       PKPCR Pcr;
-
-       DPRINT("KeInsertQueueDpc(DPC %x, SystemArgument1 %x, SystemArgument2 %x)\n",
-               Dpc, SystemArgument1, SystemArgument2);
-
-       /* 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 == KThreadedDpc) {
-       //      return FALSE;
-       //      KeLowerIrql(OldIrql);
-       //}
-
-#ifdef MP
-       /* Get the right PCR for this CPU */
-       if (Dpc->Number >= MAXIMUM_PROCESSORS) {
-               ASSERT (Dpc->Number - MAXIMUM_PROCESSORS < KeNumberProcessors);
-               Pcr = (PKPCR)(KPCR_BASE + (Dpc->Number - MAXIMUM_PROCESSORS) * PAGE_SIZE);
-       } else {
-               ASSERT (Dpc->Number < KeNumberProcessors);
-               Pcr = KeGetCurrentKPCR();
-               Dpc->Number = KeGetCurrentProcessorNumber();
-       }
-#else
-       Pcr = (PKPCR)KPCR_BASE;
-#endif
-
-       /* Get the DPC Data */
-       if (InterlockedCompareExchange((LONG*)&Dpc->DpcData, (LONG)&Pcr->PrcbData.DpcData[0].DpcLock, 0)) {
-               DPRINT("DPC Already Inserted");
-               KeLowerIrql(OldIrql);
-               return(FALSE);
-       }
-       
-       /* Now we can play with the DPC safely */
-       Dpc->SystemArgument1=SystemArgument1;
-       Dpc->SystemArgument2=SystemArgument2;
-       Pcr->PrcbData.DpcData[0].DpcQueueDepth++;
-       Pcr->PrcbData.DpcData[0].DpcCount++;
-   
-       /* Make sure the lists are free if the Queue is 0 */
-       if (Pcr->PrcbData.DpcData[0].DpcQueueDepth == 0) {
-               ASSERT(IsListEmpty(&Pcr->PrcbData.DpcData[0].DpcListHead));
-       } else {
-               ASSERT(!IsListEmpty(&Pcr->PrcbData.DpcData[0].DpcListHead));    
-       }
-       
-       /* Insert the DPC into the list. HighImportance DPCs go at the beginning  */
-       if (Dpc->Importance == HighImportance) {
-               InsertHeadList(&Pcr->PrcbData.DpcData[0].DpcListHead, &Dpc->DpcListEntry);
-       } else { 
-               InsertTailList(&Pcr->PrcbData.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 ((!Pcr->PrcbData.DpcRoutineActive) && (!Pcr->PrcbData.DpcInterruptRequested)) {
-               
-#ifdef MP      
-               /* Check if this is the same CPU */
-               if (Pcr != KeGetCurrentKPCR()) {
-                       /* Send IPI if High Importance */
-                       if ((Dpc->Importance == HighImportance) ||
-                           (Pcr->PrcbData.DpcData[0].DpcQueueDepth >= Pcr->PrcbData.MaximumDpcQueueDepth)) {
-                       
-                               /* FIXME: USE IPI */
-                               Pcr->PrcbData.DpcInterruptRequested = TRUE;
-                               HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
-                       }
-               } else {
-                       /* Request an Interrupt only if the DPC isn't low priority */
-                       if ((Dpc->Importance != LowImportance) || 
-                            (Pcr->PrcbData.DpcData[0].DpcQueueDepth >= Pcr->PrcbData.MaximumDpcQueueDepth) ||
-                               (Pcr->PrcbData.DpcRequestRate < Pcr->PrcbData.MinimumDpcRate)) {
-                       
-                               /* Request Interrupt */
-                               HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
-                               Pcr->PrcbData.DpcInterruptRequested = TRUE;
-                       }
-               }
-#else
-               DPRINT("Requesting Interrupt. Importance: %x. QueueDepth: %x. MaxQueue: %x . RequestRate: %x. MinRate:%x \n", Dpc->Importance, Pcr->PrcbData.DpcData[0].DpcQueueDepth, Pcr->PrcbData.MaximumDpcQueueDepth, Pcr->PrcbData.DpcRequestRate, Pcr->PrcbData.MinimumDpcRate);
-               /* Request an Interrupt only if the DPC isn't low priority */
-               if ((Dpc->Importance != LowImportance) || 
-                    (Pcr->PrcbData.DpcData[0].DpcQueueDepth >= Pcr->PrcbData.MaximumDpcQueueDepth) ||
-                       (Pcr->PrcbData.DpcRequestRate < Pcr->PrcbData.MinimumDpcRate)) {
-                       
-                       /* Request Interrupt */
-                       DPRINT("Requesting Interrupt\n");
-                       HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
-                       Pcr->PrcbData.DpcInterruptRequested = TRUE;
-               }
-#endif
-       }
-       /* Lower IRQL */        
-       KeLowerIrql(OldIrql);
-       return(TRUE);
+    /* Call the internal routine */
+    KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, ThreadedDpcObject);
 }
 
 /*
  * @implemented
  */
-BOOLEAN STDCALL
-KeRemoveQueueDpc (PKDPC        Dpc)
-/*
- * 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
- */
+VOID
+NTAPI
+KeInitializeDpc(IN PKDPC Dpc,
+                IN PKDEFERRED_ROUTINE DeferredRoutine,
+                IN PVOID DeferredContext)
 {
-       BOOLEAN WasInQueue;
-       KIRQL OldIrql;
-       
-       /* Raise IRQL */
-       DPRINT("Removing DPC: %x\n", Dpc);
-       KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
-       
-       /* 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);
-
-       }
-
-       /* Return if the DPC was in the queue or not */
-       KeLowerIrql(OldIrql);
-       return WasInQueue;
+    /* Call the internal routine */
+    KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, DpcObject);
 }
 
 /*
  * @implemented
  */
-VOID
-STDCALL
-KeFlushQueuedDpcs(VOID)
-/*
- * FUNCTION: 
- *          Called to Deliver DPCs if any are pending.
- * NOTES:
- *          Called when deleting a Driver.
- */
+BOOLEAN
+NTAPI
+KeInsertQueueDpc(IN PKDPC Dpc,
+                 IN PVOID SystemArgument1,
+                 IN PVOID SystemArgument2)
 {
-       if (KeGetCurrentKPCR()->PrcbData.DpcData[0].DpcQueueDepth) HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
+    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 */
+    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);
+
+    /* 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
+        {
+            /* 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;
+                    }
+                }
+            }
+        }
+    }
+
+    /* 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;
 }
 
 /*
  * @implemented
  */
-BOOLEAN 
-STDCALL
-KeIsExecutingDpc(
-       VOID
-)
+BOOLEAN
+NTAPI
+KeRemoveQueueDpc(IN PKDPC Dpc)
 {
-       return KeGetCurrentKPCR()->PrcbData.DpcRoutineActive;
+    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;
 }
 
 /*
- * FUNCTION: Specifies the DPCs importance
- * ARGUMENTS:
- *          Dpc = Initalizes DPC
- *          Importance = DPC importance
- * RETURNS: None
- *
  * @implemented
  */
-VOID 
-STDCALL
-KeSetImportanceDpc (IN PKDPC           Dpc,
-                   IN  KDPC_IMPORTANCE Importance)
+VOID
+NTAPI
+KeFlushQueuedDpcs(VOID)
 {
-       Dpc->Importance = Importance;
+    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);
+    }
 }
 
 /*
- * FUNCTION: Specifies on which processor the DPC will run
- * ARGUMENTS:
- *          Dpc = Initalizes DPC
- *          Number = Processor number
- * RETURNS: None
- *
  * @implemented
  */
-VOID STDCALL
-KeSetTargetProcessorDpc (IN    PKDPC   Dpc,
-                        IN     CCHAR   Number)
+BOOLEAN
+NTAPI
+KeIsExecutingDpc(VOID)
 {
-   if (Number >= MAXIMUM_PROCESSORS)
-   {
-      Dpc->Number = 0;
-   }
-   else
-   {
-      ASSERT(Number < KeNumberProcessors);
-      Dpc->Number = Number + MAXIMUM_PROCESSORS;
-   }
+    /* Return if the Dpc Routine is active */
+    return KeGetCurrentPrcb()->DpcRoutineActive;
 }
 
-VOID
-STDCALL
-KiQuantumEnd(VOID)
 /*
- * 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.
+ * @implemented
  */
+VOID
+NTAPI
+KeSetImportanceDpc (IN PKDPC Dpc,
+                    IN KDPC_IMPORTANCE Importance)
 {
-       KPRCB Prcb;
-       PKTHREAD CurrentThread;
-       KIRQL OldIrql;
-       PKPROCESS Process;
-       KPRIORITY OldPriority;
-       KPRIORITY NewPriority;
-       
-       /* Lock dispatcher, get current thread */
-       Prcb = KeGetCurrentKPCR()->PrcbData;
-       CurrentThread = KeGetCurrentThread();
-       OldIrql = KeRaiseIrqlToSynchLevel();
-       
-       /* Get the Thread's Process */
-       Process = CurrentThread->ApcState.Process;
-       
-       /* Set DPC Event if requested */
-       if (Prcb.DpcSetEventRequest) {
-               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) {
-                       NewPriority = OldPriority - CurrentThread->PriorityDecrement - 1;
-                       if (NewPriority < CurrentThread->BasePriority) {
-                               NewPriority = CurrentThread->BasePriority;
-                       }
-                       CurrentThread->PriorityDecrement = 0;
-                       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);
-       PsDispatchThread(THREAD_STATE_READY);
-}      
+    /* Set the DPC Importance */
+    ASSERT_DPC(Dpc);
+    Dpc->Importance = Importance;
+}
 
 /*
  * @implemented
  */
 VOID
-STDCALL
-KiDispatchInterrupt(VOID)
-/*
- * FUNCTION: 
- *          Called whenever a system interrupt is generated at DISPATCH_LEVEL.
- *          It delivers queued DPCs and dispatches a new thread if need be.
- */
+NTAPI
+KeSetTargetProcessorDpc(IN PKDPC Dpc,
+                        IN CCHAR Number)
 {
-       PLIST_ENTRY DpcEntry;
-       PKDPC Dpc;
-       KIRQL OldIrql;
-       PKPCR Pcr;
-
-       DPRINT("Dispatching Interrupts\n");
-       ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
-
-       /* Set DPC Deliver to Active */
-       Pcr = KeGetCurrentKPCR();
-       Pcr->PrcbData.DpcRoutineActive = TRUE;
-
-       if (Pcr->PrcbData.DpcData[0].DpcQueueDepth > 0) {
-               /* Raise IRQL */
-               KeRaiseIrql(HIGH_LEVEL, &OldIrql);
-#ifdef MP              
-               KiAcquireSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock);
-#endif
-
-               DPRINT("&Pcr->PrcbData.DpcData[0].DpcListHead: %x\n", &Pcr->PrcbData.DpcData[0].DpcListHead);
-               /* Loop while we have entries */
-               while (!IsListEmpty(&Pcr->PrcbData.DpcData[0].DpcListHead)) {
-                       ASSERT(Pcr->PrcbData.DpcData[0].DpcQueueDepth > 0);
-                       DPRINT("Queue Depth: %x\n", Pcr->PrcbData.DpcData[0].DpcQueueDepth);
-                       
-                       /* Get the DPC call it */
-                       DpcEntry = RemoveHeadList(&Pcr->PrcbData.DpcData[0].DpcListHead);
-                       Dpc = CONTAINING_RECORD(DpcEntry, KDPC, DpcListEntry);
-                       DPRINT("Dpc->DpcListEntry.Flink %x\n", Dpc->DpcListEntry.Flink);
-                       Dpc->DpcData = NULL;
-                       Pcr->PrcbData.DpcData[0].DpcQueueDepth--;
-#ifdef MP
-                       KiReleaseSpinLock(&Pcr->PrcbData.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 MP
-                       KiAcquireSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock);
-#endif
-                       
-#ifdef MP
-                       /* 
-                       * 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 (Pcr != KeGetCurrentKPCR()) {
-                               KiReleaseSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock);
-                               Pcr = KeGetCurrentKPCR();
-                               KiAcquireSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock);
-                       }
-#endif
-               }
-#ifdef MP
-                       KiReleaseSpinLock(&Pcr->PrcbData.DpcData[0].DpcLock);
-#endif
-               /* Clear DPC Flags */
-               Pcr->PrcbData.DpcRoutineActive = FALSE;
-               Pcr->PrcbData.DpcInterruptRequested = FALSE;
-               
-               /* DPC Dispatching Ended, re-enable interrupts */
-               KeLowerIrql(OldIrql);
-       }
-       
-       DPRINT("Checking for Quantum End\n");
-       /* If we have Quantum End, call the function */
-       if (Pcr->PrcbData.QuantumEnd) {
-               Pcr->PrcbData.QuantumEnd = FALSE;
-               KiQuantumEnd();
-       }
+    /* Set a target CPU */
+    ASSERT_DPC(Dpc);
+    Dpc->Number = Number + MAXIMUM_PROCESSORS;
 }
 
 /* EOF */