Garantieren des Vorwärtsfortschritts von E/A-Vorgängen

Einige Treiber, z. B. Speichertreiber für das Auslagerungsgerät des Systems, müssen mindestens einige ihrer unterstützten E/A-Vorgänge ohne Fehler ausführen, um den Verlust kritischer Systemdaten zu vermeiden. Eine mögliche Ursache für einen Treiberfehler ist eine Situation mit wenig Arbeitsspeicher. Wenn das Framework oder der Treiber nicht genügend Arbeitsspeicher für die Verarbeitung einer E/A-Anforderung zuweisen kann, muss der eine oder der andere die E/A-Anforderung möglicherweise fehlschlagen, indem er sie mit einem Fehlerstatuswert abschließt .

In KMDF-Versionen vor Version 1.9 schlägt das Framework eine E/A-Anforderung immer fehl, wenn es kein Frameworkanforderungsobjekt für ein E/A-Anforderungspaket (IRP) zuordnen kann, das der E/A-Manager an den Treiber gesendet hat. Damit Treiber E/A-Anforderungen in Situationen mit wenig Arbeitsspeicher verarbeiten können, bieten Versionen 1.9 und höher des Frameworks eine garantierte Vorwärtsstatusfunktion für E/A-Warteschlangen.

Diese Funktion ermöglicht es dem Framework und dem Treiber, Arbeitsspeicher für Gruppen von Anforderungsobjekten bzw. anforderungsbezogene Treiberkontextpuffer vorab zuzuweisen. Das Framework und der Treiber verwenden diesen vorab zugewiesenen Arbeitsspeicher nur, wenn der Systemarbeitsspeicher gering ist.

Features des garantierten Vorwärtsfortschritts

Mithilfe des garantierten Vorwärtsfortschritts des Frameworks für E/A-Warteschlangen kann ein Treiber:

  • Bitten Sie das Framework, eine Reihe von Anforderungsobjekten vorab zuzuweisen, die in Situationen mit wenig Arbeitsspeicher mit einer bestimmten E/A-Warteschlange verwendet werden sollen.

  • Stellen Sie eine Rückruffunktion bereit, die anforderungsspezifische Ressourcen vorab zuordnet, die der Treiber verwenden kann, wenn er vorab zugewiesene Anforderungsobjekte vom Framework in Situationen mit wenig Arbeitsspeicher empfängt.

  • Stellen Sie eine weitere Rückruffunktion bereit, die treiberspezifische Ressourcen für eine E/A-Anforderung ordnet, wenn eine Situation mit wenig Arbeitsspeicher nicht erkannt wurde. Wenn die Zuordnung dieser Rückruffunktion aufgrund einer Situation mit wenig Arbeitsspeicher fehlschlägt, kann sie angeben, ob das Framework eines seiner vorab zugewiesenen Anforderungsobjekte verwenden soll.

  • Geben Sie an, welche E/A-Anforderungen die Verwendung von vorab zugeordneten Anforderungsobjekten erfordern. Zu den Optionen gehören die Verwendung vorab zugeordneter Objekte für alle IRPs, deren Verwendung nur, wenn ein Auslagerungs-E/A-Vorgang ausgeführt wird, oder die Überprüfung jedes IRP durch eine zusätzliche Treiberrückruffunktion, um zu bestimmen, ob ein vorab zugeordnetes Objekt verwendet werden soll.

Wenn Ihr Treiber einen garantierten Vorwärtsfortschritt für eine oder mehrere seiner E/A-Warteschlangen implementiert, ist der Treiber besser in der Lage, E/A-Anforderungen in Situationen mit wenig Arbeitsspeicher erfolgreich zu verarbeiten. Sie können einen garantierten Vorwärtsfortschritt für die Standard-E/A-Warteschlange eines Geräts und für jede E/A-Warteschlange implementieren, die Ihr Treiber konfiguriert, indem Sie WdfDeviceConfigureRequestDispatching aufrufen.

Die garantierte Vorwärtsfortschrittsfunktion des Frameworks funktioniert nur für Ihren Treiber, wenn sowohl Ihr Treiber als auch die E/A-Ziele des Treibers einen garantierten Vorwärtsfortschritt implementieren. Anders ausgedrückt: Wenn ein Treiber einen garantierten Vorwärtsfortschritt für ein Gerät implementiert, müssen alle Treiber auf niedrigerer Ebene im Treiberstapel des Geräts auch einen garantierten Vorwärtsfortschritt implementieren.

Aktivieren des garantierten Vorwärtsfortschritts für eine E/A-Warteschlange

Um den garantierten Vorwärtsfortschritt für eine E/A-Warteschlange zu aktivieren, initialisiert Ihr Treiber eine WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY-Struktur und ruft dann die WdfIoQueueAssignForwardProgressPolicy-Methode auf. Wenn der Treiber WdfDeviceConfigureRequestDispatching aufruft , um eine E/A-Warteschlange zu konfigurieren, muss er dies tun, bevor er WdfIoQueueAssignForwardProgressPolicy aufruft.

Wenn der Treiber WdfIoQueueAssignForwardProgressPolicy aufruft, kann er die folgenden drei Ereignisrückruffunktionen angeben, die alle optional sind:

EvtIoAllocateResourcesForReservedRequest
Die Rückruffunktion EvtIoAllocateResourcesForReservedRequest eines Treibers reserviert anforderungsspezifische Ressourcen für Anforderungsobjekte, die das Framework für Situationen mit geringem Arbeitsspeicher reserviert.

Das Framework ruft diese Rückruffunktion bei jedem Erstellen eines reservierten Anforderungsobjekts auf. Der Treiber sollte anforderungsspezifische Ressourcen für eine E/A-Anforderung zuordnen, in der Regel mithilfe des Kontextbereichs des reservierten Anforderungsobjekts.

EvtIoAllocateRequestResources
Die EvtIoAllocateRequestResources-Rückruffunktion eines Treibers ordnet anforderungsspezifische Ressourcen für die sofortige Verwendung zu. Er wird sofort aufgerufen, nachdem das Framework ein IRP empfangen und ein Anforderungsobjekt für das IRP erstellt hat.

Wenn der Versuch der Rückruffunktion, Ressourcen zuzuordnen, fehlschlägt, gibt die Rückruffunktion einen Fehlerstatuswert zurück. Das Framework löscht dann das neu erstellte Anforderungsobjekt und verwendet eines seiner reservierten Anforderungsobjekte. Der Anforderungshandler des Treibers wiederum verwendet anforderungsspezifische Ressourcen, die seiner EvtIoAllocateRequestResources-Rückruffunktion zuvor zugeordnet wurden.

EvtIoWdmIrpForForwardProgress
Die EvtIoWdmIrpForForwardProgress-Rückruffunktion eines Treibers untersucht ein IRP und teilt dem Framework mit, ob ein reserviertes Anforderungsobjekt für das IRP verwendet werden soll oder die E/A-Anforderung fehlschlägt, indem ein Fehlerstatuswert angegeben wird.

Das Framework ruft diese Rückruffunktion nur auf, wenn das Framework kein neues Anforderungsobjekt erstellen kann und Sie (durch Festlegen eines Flags in der WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY struktur des Treibers) angegeben haben, dass der Treiber IRPs in Situationen mit geringem Arbeitsspeicher untersuchen soll. Anders ausgedrückt: Ihr Treiber kann jedes IRP bewerten und entscheiden, ob es sich um eine Handelt, die auch in Situationen mit wenig Arbeitsspeicher verarbeitet werden muss.

Wenn Ihr Treiber WdfIoQueueAssignForwardProgressPolicy aufruft, gibt er auch die Anzahl der reservierten Anforderungsobjekte an, die das Framework für Situationen mit geringem Arbeitsspeicher vorab zuordnen soll. Sie können die Anzahl der Anforderungsobjekte auswählen, die für Ihr Gerät und Ihren Treiber geeignet sind. Um Leistungseinbußen zu verhindern, sollte Ihr Treiber in der Regel eine Zahl angeben, die der Anzahl der E/A-Anforderungen entspricht, die der Treiber und das Gerät parallel verarbeiten können.

Wenn der Aufruf Ihres Treibers von WdfIoQueueAssignForwardProgressPolicy und der zugehörigen EvtIoAllocateResourcesForReservedRequest-Rückruffunktion jedoch zu viele reservierte Anforderungsobjekte oder zu viel anforderungsspezifischen Ressourcenspeicher vorab zuordnen, kann Ihr Treiber tatsächlich zu den Situationen mit geringem Arbeitsspeicher beitragen, die Sie behandeln möchten. Sie sollten die Leistung Ihres Treibers und Geräts testen und Simulationen mit geringem Arbeitsspeicher einbeziehen, um die am besten zu wählenden Zahlen zu ermitteln.

Bevor WdfIoQueueAssignForwardProgressPolicy zurückgibt, erstellt und reserviert das Framework die Anzahl von Anforderungsobjekten, die der Treiber angegeben hat. Jedes Mal, wenn ein Anforderungsobjekt reserviert wird, ruft das Framework sofort die Rückruffunktion EvtIoAllocateResourcesForReservedRequest des Treibers auf, damit der Treiber anforderungsspezifische Ressourcen zuordnen und speichern kann, falls das Framework tatsächlich die reservierten Anforderungsobjekte verwendet.

Wenn einer der Anforderungshandler des Treibers eine E/A-Anforderung von der E/A-Warteschlange empfängt, kann er die WdfRequestIsReserved-Methode aufrufen, um zu bestimmen, ob das Anforderungsobjekt eines ist, das das Framework für Situationen mit geringem Arbeitsspeicher vorab zugeordnet hat. Wenn diese Methode TRUE zurückgibt, sollte der Treiber Ressourcen verwenden, die seine EvtIoAllocateResourcesForReservedRequest-Rückruffunktion reserviert hat.

Wenn das Framework eines seiner reservierten Anforderungsobjekte verwendet, gibt es das Objekt an seinen Satz reservierter Objekte zurück, nachdem der Treiber die Anforderung abgeschlossen hat. Das Framework speichert das Anforderungsobjekt und alle Kontextbereiche, die der Treiber durch Aufrufen von WdfDeviceInitSetRequestAttributes oder WdfObjectAllocateContext erstellt hat, zur Wiederverwendung, wenn eine andere Situation mit wenig Arbeitsspeicher auftritt.

So wird der Fortschritt des Frameworks und der Treiberunterstützung garantiert

Im Folgenden sind die Schritte aufgeführt, die der Treiber und das Framework ausführen, um den garantierten Vorwärtsfortschritt für eine E/A-Warteschlange zu unterstützen:

  1. Der Treiber ruft WdfIoQueueAssignForwardProgressPolicy auf.

    Als Reaktion darauf ordnet das Framework die Anzahl von Anforderungsobjekten zu und speichert sie, die der Treiber angibt. Wenn der Treiber zuvor WdfDeviceInitSetRequestAttributes aufgerufen hat, enthält jede Zuordnung Kontextraum, den WdfDeviceInitSetRequestAttributes angegeben hat.

    Wenn der Treiber außerdem eine EvtIoAllocateResourcesForReservedRequest-Rückruffunktion bereitgestellt hat, ruft das Framework die Rückruffunktion jedes Mal auf, wenn es ein Anforderungsobjekt zuordnet und speichert.

  2. Das Framework empfängt ein E/A-Anforderungspaket (IRP), das der E/A-Manager an den Treiber sendet.

    Das Framework versucht, ein Anforderungsobjekt für das IRP zuzuordnen. Wenn die E/A-Warteschlange, die der Treiber für den Anforderungstyp erstellt hat, einen garantierten Vorwärtsfortschritt unterstützt, hängt der nächste Schritt davon ab, ob die Zuordnung erfolgreich ist oder fehlschlägt:

    • Die Anforderungsobjektzuordnung ist erfolgreich.

      Wenn der Treiber eine EvtIoAllocateRequestResources-Rückruffunktion bereitgestellt hat , ruft das Framework sie auf. Wenn die Rückruffunktion STATUS_SUCCESS zurückgibt, fügt das Framework die Anforderung der E/A-Warteschlange hinzu. Wenn die Rückruffunktion einen Fehlerstatuswert zurückgibt, löscht das Framework das soeben erstellte Anforderungsobjekt und verwendet eines seiner vorab zugeordneten Anforderungsobjekte. Wenn der Anforderungshandler des Treibers das Anforderungsobjekt empfängt, bestimmt er, ob das Anforderungsobjekt vorab zugeordnet war und daher die vorab zugewiesenen Ressourcen des Treibers verwenden soll.

      Wenn der Treiber keineEvtIoAllocateRequestResources-Rückruffunktion bereitgestellt hat, fügt das Framework die Anforderung der E/A-Warteschlange hinzu, so als ob der Treiber den garantierten Vorwärtsfortschritt nicht aktiviert hätte.

    • Die Zuordnung des Anforderungsobjekts schlägt fehl.

      Was das Framework als Nächstes ausführt, hängt vom Wert ab, den der Treiber für den ForwardProgressReservedPolicy-Member der WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY-Struktur bereitgestellt hat. Dieses Element informiert das Framework, wann eine reservierte Anforderung verwendet werden soll: immer nur, wenn die E/A-Anforderung ein Auslagerungs-E/A-Vorgang ist, oder nur, wenn die Rückruffunktion EvtIoWdmIrpForForwardProgress angibt, dass eine reservierte Anforderung verwendet werden soll.

    In allen Fällen können die Anforderungshandler des Treibers WdfRequestIsReserved aufrufen, um zu bestimmen, ob das Framework ein reserviertes Anforderungsobjekt verwendet hat. Wenn dies der Fall ist, sollte der Treiber die Anforderungsressourcen verwenden, die seiner Rückruffunktion EvtIoAllocateResourcesForReservedRequest zugeordnet sind.

Szenario "Garantierter Vorwärtsfortschritt"

Sie schreiben einen Treiber für ein Speichergerät, das möglicherweise die Auslagerungsdatei des Systems enthält. Es ist wichtig, dass Lesevorgänge aus und Schreibvorgänge in die Auslagerungsdatei erfolgreich sind.

Sie entscheiden sich, separate E/A-Warteschlangen für Lese- und Schreibvorgänge zu erstellen und einen garantierten Vorwärtsfortschritt für beide E/A-Warteschlangen zu aktivieren. Sie entscheiden sich, eine dritte E/A-Warteschlange für alle anderen Anforderungstypen zu erstellen, ohne den garantierten Vorwärtsfortschritt zu aktivieren.

Ihr Treiberstapel und Ihr Gerät können vier Schreibvorgänge parallel verarbeiten. Daher legen Sie den TotalForwardProgressRequests-Member der WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY-Struktur auf 4 fest, bevor Sie WdfIoQueueAssignForwardProgressPolicy aufrufen.

Sie entscheiden, dass die Gewährleistung des Vorwärtsfortschritts nur wichtig ist, wenn das Gerät Ihres Treibers das Auslagerungsgerät ist. Daher legt Ihr Treiber den ForwardProgressReservedPolicy-Member der WDF_IO_QUEUE_FORWARD_PROGRESS_POLICY-Struktur auf WdfIoForwardProgressReservedPolicyPagingIO fest.

Da Ihr Treiber für jede Leseanforderung und jede Schreibanforderung ein Frameworkspeicherobjekt erfordert, entscheiden Sie, dass Ihr Treiber einige Speicherobjekte vorab zuordnen sollte, die für die Aufrufe von WdfIoTargetFormatRequestForRead und WdfIoTargetFormatRequestForWrite in Situationen mit geringem Arbeitsspeicher verwendet werden sollen.

Daher stellt der Treiber eine EvtIoAllocateResourcesForReservedRequest-Rückruffunktion für die Lesewarteschlange und eine weitere für die Schreibwarteschlange bereit. Jedes Mal, wenn das Framework eine dieser Rückruffunktionen aufruft, ruft die Rückruffunktion WdfMemoryCreate auf und speichert das zurückgegebene Objekthandle für Situationen mit geringem Arbeitsspeicher. Da die Rückruffunktion ein Handle für ein vorab zugeordnetes Anforderungsobjekt empfängt, kann sie das Speicherobjekt dem Anforderungsobjekt zuordnen. (Ein Treiber für ein DMA-Gerät kann auch Framework-DMA-Objekte vorab zuordnen.)

Die Anforderungshandler für die Lese- und Schreibwarteschlangen müssen bestimmen, ob jedes empfangene Anforderungsobjekt eines ist, das das Framework für Situationen mit geringem Arbeitsspeicher reserviert hat. Ein Anforderungshandler kann WdfRequestIsReserved aufrufen oder das Anforderungsobjekthandle mit dem vergleichen, das zuvor von der Rückruffunktion EvtIoAllocateResourcesForReservedRequest empfangen wurde.

Der Treiber stellt außerdem eine EvtIoAllocateRequestResources-Rückruffunktion für die Lesewarteschlange und eine weitere für die Schreibwarteschlange bereit. Das Framework ruft eine dieser Rückruffunktionen auf, wenn es eine Lese- oder Schreibanforderung vom E/A-Manager empfängt und erfolgreich ein Anforderungsobjekt erstellt. Jede dieser Rückruffunktionen ruft WdfMemoryCreate auf, um ein Speicherobjekt für eine Anforderung zuzuweisen. Wenn die Zuordnung fehlschlägt, gibt die Rückruffunktion einen Fehlerstatuswert zurück, um das Framework zu benachrichtigen, dass gerade eine Situation mit wenig Arbeitsspeicher aufgetreten ist. Das Framework erkennt den Fehlerrückgabewert und löscht das soeben erstellte Anforderungsobjekt und verwendet eines seiner vorab zugeordneten Objekte.

Dieser Treiber stellt keine EvtIoWdmIrpForForforwardProgress-Rückruffunktion bereit, da er einzelne Lese- oder Schreib-IRPs nicht untersuchen muss, bevor das Framework sie einer E/A-Warteschlange hinzufügt.

Wenn ein Treiber einen garantierten Vorwärtsfortschritt für ein Gerät implementiert, müssen alle Treiber auf niedrigerer Ebene im Treiberstapel des Geräts auch einen garantierten Vorwärtsfortschritt implementieren.