DPC et traitement différé dans un driver WDM

Ce cours dédiée au développement de drivers en kernel mode sans Framework

Moderator: Rick

Post Reply
Hydraxx
Site Admin
Posts: 144
Joined: Mon Jan 12, 2026 4:04 pm
Location: France
Contact:

DPC et traitement différé dans un driver WDM

Post by Hydraxx »

DPC et traitement différé dans un driver WDM

Introduction

Une ISR doit rester très courte. Pourtant, après une interruption, le driver doit souvent effectuer davantage de travail : terminer un IRP, lancer l'opération suivante, traiter les données reçues, mettre à jour des structures internes, etc.

Windows fournit pour cela les Deferred Procedure Calls, ou DPC.

1. Principe

Code: Select all

matériel
   |
   v
interruption
   |
   v
ISR à DIRQL
   |
   v
mise en file d'un DPC
   |
   v
DPC à DISPATCH_LEVEL
   |
   v
traitement différé
Le DPC permet donc de sortir rapidement de l'ISR tout en exécutant ensuite du code à haute priorité.

2. DPC associé à un DEVICE_OBJECT

WDM fournit un DPC intégré au DEVICE_OBJECT.

Initialisation :

Code: Select all

IoInitializeDpcRequest(
    DeviceObject,
    DpcRoutine
);
Demande depuis l'ISR :

Code: Select all

IoRequestDpc(
    DeviceObject,
    CurrentIrp,
    Context
);
API importantes
  • IoInitializeDpcRequest
  • IoRequestDpc
  • KeInitializeDpc
  • KeInsertQueueDpc
  • KeRemoveQueueDpc
  • KeSetImportanceDpc
  • KeSetTargetProcessorDpc
3. Prototype du DPC associé au périphérique

Code: Select all

VOID DpcRoutine(
    PKDPC Dpc,
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
);
Le DPC peut alors effectuer une partie du traitement qui était trop lourde pour l'ISR.

4. IRQL d'un DPC

Un DPC s'exécute à DISPATCH_LEVEL.

Cela impose plusieurs contraintes :
  • Pas de mémoire pageable.
  • Pas d'attente bloquante sur des objets nécessitant PASSIVE_LEVEL.
  • Temps d'exécution court.
  • Attention aux spin locks et à la concurrence multi-CPU.
Un DPC est moins contraint qu'une ISR mais reste du code haute priorité.

5. Terminer un IRP depuis un DPC

Un schéma classique :

Code: Select all

VOID DpcRoutine(
    PKDPC Dpc,
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
)
{
    UNREFERENCED_PARAMETER(Dpc);
    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Context);

    if (Irp)
    {
        Irp->IoStatus.Status = STATUS_SUCCESS;
        Irp->IoStatus.Information = 128;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
    }
}
6. Un DPC peut-il être demandé plusieurs fois ?

Un objet DPC donné ne peut pas être présent plusieurs fois simultanément dans la file DPC.

Si plusieurs interruptions arrivent avant son exécution, le driver doit gérer la possibilité que plusieurs événements aient été accumulés.

Exemple :

Code: Select all

volatile LONG PendingEvents;
L'ISR incrémente le compteur, puis le DPC traite tous les événements en attente.

7. DPC custom avec KDPC

Le driver peut créer son propre objet KDPC.

Code: Select all

KDPC MyDpc;

KeInitializeDpc(
    &MyDpc,
    MyDpcRoutine,
    context
);
Pour le mettre en file :

Code: Select all

KeInsertQueueDpc(
    &MyDpc,
    arg1,
    arg2
);
Pour tenter de le retirer avant exécution :

Code: Select all

KeRemoveQueueDpc(&MyDpc);
8. Importance d'un DPC

Windows permet de modifier l'importance d'un DPC :

Code: Select all

KeSetImportanceDpc(
    &MyDpc,
    HighImportance
);
Il faut éviter de monter artificiellement la priorité sans justification. Un DPC trop fréquent ou trop long peut dégrader fortement la latence du système.

9. Affinité CPU

Un DPC peut être ciblé vers un processeur particulier.

Historiquement :

Code: Select all

KeSetTargetProcessorDpc(...)
La raison principale est la cohérence et la localité : certains drivers préfèrent exécuter leur DPC sur le même CPU que l'interruption ou sur un CPU défini.

10. Synchronisation ISR / DPC

L'ISR et le DPC peuvent accéder aux mêmes données.

Il faut éviter :

Code: Select all

ISR écrit structure X
DPC lit structure X
au même moment sur un autre CPU
Solutions possibles selon le cas :
  • Interlocked* pour des compteurs simples.
  • Spin locks adaptés à l'IRQL.
  • KeSynchronizeExecution pour les données partagées avec l'ISR.
  • Organisation lock-free soigneuse pour certains chemins très rapides.
11. Exemple complet ISR -> DPC

Code: Select all

BOOLEAN IsrRoutine(PKINTERRUPT Interrupt, PVOID Context)
{
    UNREFERENCED_PARAMETER(Interrupt);

    PDEVICE_EXTENSION devExt =
        (PDEVICE_EXTENSION)Context;

    ULONG status = READ_REGISTER_ULONG(
        devExt->StatusRegister
    );

    if (!(status & INT_PENDING))
        return FALSE;

    WRITE_REGISTER_ULONG(
        devExt->StatusRegister,
        INT_ACK
    );

    IoRequestDpc(
        devExt->DeviceObject,
        devExt->CurrentIrp,
        NULL
    );

    return TRUE;
}

VOID DpcRoutine(
    PKDPC Dpc,
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
)
{
    UNREFERENCED_PARAMETER(Dpc);
    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Context);

    if (!Irp)
        return;

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 512;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
12. Quand utiliser un Work Item plutôt qu'un DPC ?

Si le traitement doit :
  • Attendre.
  • Accéder à du code pageable.
  • Exécuter une opération nécessitant PASSIVE_LEVEL.
  • Effectuer un traitement long.
Il vaut souvent mieux différer encore vers un Work Item ou un thread système.

Conceptuellement :

Code: Select all

ISR
 |
 v
DPC
 |
 v
Work Item / thread
13. Résumé

Le DPC constitue le pont entre l'ISR très courte et le traitement de fond du driver.

Code: Select all

ISR = reconnaître et acquitter
DPC = traiter rapidement la conséquence
Work Item = traitement pouvant attendre / PASSIVE_LEVEL

Who is online

Users browsing this forum: No registered users and 1 guest