Deferred Procedure Call (DPC)
A Deferred Procedure Call (DPC) is a mechanism used in Windows kernel-mode drivers to execute code at a lower interrupt request level (IRQL) than the interrupt service routine (ISR) that requested it.
DPCS are essential for managing hardware interrupts efficiently. When an interrupt occurs, the ISR is executed immediately at a high IRQL. However, ISRs should be kept as short as possible to avoid delaying other critical system operations. DPCS allow the bulk of the interrupt handling logic to be deferred to a later time when the system is less busy and can execute code at a lower IRQL.
Why Use DPCS?
- Reduce ISR Latency: By offloading work from the ISR, DPCS minimize the time spent at a high IRQL, allowing other high-priority interrupts to be serviced promptly.
- Simplify ISR Logic: Complex or time-consuming operations can be moved out of the ISR, making it cleaner and easier to manage.
- System Resource Management: DPCS execute at a lower IRQL (typically `PASSIVE_LEVEL` or `DISPATCH_LEVEL`), allowing them to access resources that are not available at higher IRQLs.
DPC Objects and Functions
DPCS are managed using the DPC object. Drivers typically create and initialize a DPC object, then associate a DPC routine with it.
Key Structures and Functions:
KDPC: The DPC object structure.KeInitializeDpc: Initializes aKDPCobject.KeSetDpcRoutine: Sets the DPC routine that will be executed for the DPC object.KeInsertQueueDpc: Queues a DPC object for execution. This is typically called from an ISR.
DPC Routine
The DPC routine is the function that contains the code to be executed asynchronously after the ISR completes. It runs at `DISPATCH_LEVEL` IRQL.
Signature of a DPC Routine:
VOID
MyDpcRoutine(
_In_ PKDPC Dpc,
_In_opt_ PVOID DeferredContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2
);
Dpc: A pointer to theKDPCobject for which the routine is executing.DeferredContext: A context pointer passed when the DPC was queued.SystemArgument1andSystemArgument2: Additional context pointers.
DPC Execution Flow
- An interrupt occurs, and the ISR is executed at a high IRQL.
- The ISR performs minimal necessary hardware acknowledgment and then queues a DPC for execution.
- The ISR returns, and the system's interrupt handling mechanism is invoked.
- When the system's IRQL drops to or below `DISPATCH_LEVEL`, and the DPC is at the head of the DPC queue, the associated DPC routine is called.
- The DPC routine executes its deferred processing logic.
Example: Queuing a DPC from an ISR
Here's a simplified conceptual example of how a DPC might be queued from an ISR:
// Assume these are global or driver-defined
KDPC MyDpcObject;
PVOID MyDeviceContext; // Context for the DPC routine
// ISR routine
BOOLEAN
MyInterruptServiceRoutine(
_In_ PKINTERRUPT Interrupt,
_In_opt_ PVOID ServiceContext
)
{
UNREFERENCED_PARAMETER(Interrupt);
UNREFERENCED_PARAMETER(ServiceContext);
// Acknowledge hardware interrupt
// ...
// Queue the DPC for deferred processing
if (KeInsertQueueDpc(&MyDpcObject, MyDeviceContext, NULL)) {
// DPC successfully queued
return TRUE; // Interrupt was handled
} else {
// DPC was already queued or couldn't be queued
return FALSE; // Interrupt might need further handling or was spurious
}
}
// DPC routine
VOID
MyDpcRoutine(
_In_ PKDPC Dpc,
_In_opt_ PVOID DeferredContext,
_In_opt_ PVOID SystemArgument1,
_In_opt_ PVOID SystemArgument2
)
{
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemArgument1);
UNREFERENCED_PARAMETER(SystemArgument2);
PVOID deviceContext = DeferredContext;
// Perform deferred processing using deviceContext
// This code runs at DISPATCH_LEVEL
// ...
// e.g., update device status, signal completion, etc.
}
// In DriverEntry or AddDevice:
// Initialize DPC object
// KeInitializeDpc(&MyDpcObject, MyDpcRoutine, DriverObject);
// Set context
// MyDpcObject.DeferredContext = &MyDeviceContext;
// ... then set up the interrupt object to call MyInterruptServiceRoutine
DPC Considerations
- Reentrancy: DPC routines are not inherently reentrant. If multiple interrupts can occur before a DPC finishes, you must ensure your DPC logic can handle this, possibly using synchronization mechanisms.
- Timer DPCS: DPCS can also be associated with timer objects (
KTIMER) to execute code after a specified delay, independent of hardware interrupts. - Priority: DPCS are processed based on their queue order and system load. For time-critical operations, consider alternatives like threaded DPCs if available and appropriate.
Understanding and correctly implementing DPCS is fundamental for writing efficient and stable Windows kernel-mode drivers.