Implementieren einer IoCompletion-Routine
Bei der Eingabe empfängt eine IoCompletion-Routine einen Kontextzeiger . Wenn eine Dispatchroutine IoSetCompletionRoutine aufruft, kann sie einen Kontextzeiger bereitstellen. Dieser Zeiger kann auf alle vom Treiber bestimmten Kontextinformationen verweisen, die die IoCompletion-Routine zum Verarbeiten eines IRP benötigt. Beachten Sie, dass der Kontextbereich nicht ausgelagert werden kann, da die IoCompletion-Routine unter IRQL = DISPATCH_LEVEL aufgerufen werden kann.
Beachten Sie die folgenden Implementierungsrichtlinien für IoCompletion-Routinen:
Eine IoCompletion-Routine kann den E/A-status block des IRP überprüfen, um das Ergebnis des E/A-Vorgangs zu ermitteln.
Wenn die Eingabe-IRP von der Dispatchroutine mithilfe von IoAllocateIrp oder IoBuildAsynchronousFsdRequest zugeordnet wurde, muss die IoCompletion-RoutineIoFreeIrp aufrufen, um dieses IRP freizugeben, vorzugsweise bevor die ursprüngliche IRP abgeschlossen wird.
Die IoCompletion-Routine muss alle einzelnen IRP-Ressourcen freigeben, die der vom Treiber zugewiesenen IRP zugeordnet sind, vorzugsweise bevor die entsprechende IRP freigegeben wird.
Wenn die Dispatchroutine z. B. eine MDL mit IoAllocateMdl zuordnet und IoBuildPartialMdl für eine teilvermittelte IRP aufruft, muss die IoCompletion-Routine die MDL mit IoFreeMdl freigeben. Wenn ressourcen zugeordnet werden, um den Zustand des ursprünglichen IRP beizubehalten, müssen diese Ressourcen freigegeben werden, vorzugsweise bevor IoCompleteRequest mit dem ursprünglichen IRP und definitiv bevor die Steuerung zurückgegeben wird.
Im Allgemeinen sollte die IoCompletion-Routine vor dem Freigeben oder Abschließen eines IRP alle von der Dispatch-Routine zugeordneten Ressourcen pro IRP freigeben. Andernfalls muss der Treiber den Zustand der freizugebenden Ressourcen beibehalten, bevor seine IoCompletion-Routine die Kontrolle über das Abschließen der ursprünglichen Anforderung zurückgibt.
Wenn die IoCompletion-Routine die ursprüngliche IRP mit STATUS_SUCCESS nicht abschließen kann, muss sie den E/A-status-Block im ursprünglichen IRP auf den Wert festlegen, der im vom Treiber zugewiesenen IRP zurückgegeben wurde, der dazu geführt hat, dass die IoCompletion-Routine bei der ursprünglichen Anforderung fehlschlägt.
Wenn die IoCompletion-Routine die ursprüngliche Anforderung mit STATUS_PENDING abschließt, muss ioMarkIrpPending mit dem ursprünglichen IRP aufgerufen werden, bevor IoCompleteRequest aufgerufen wird.
Wenn die IoCompletion-Routine den ursprünglichen IRP mit einem Fehler STATUS_XXX fehlschlagen muss, kann ein Fehler protokolliert werden. Es liegt jedoch in der Verantwortung des zugrunde liegenden Gerätetreibers, alle auftretenden Geräte-E/A-Fehler zu protokollieren, sodass IoCompletion-Routinen in der Regel keine Fehler protokollieren.
Wenn die IoCompletion-Routine den vom Treiber zugewiesenen IRP verarbeitet und freigegeben hat, muss die Routine die Steuerung mit STATUS_MORE_PROCESSING_REQUIRED zurückgeben.
Wenn STATUS_MORE_PROCESSING_REQUIRED aus der IoCompletion-Routine zurückgegeben wird, wird die E/A-Abschlussverarbeitung des E/A-Managers für einen vom Treiber zugewiesenen und freigegebenen IRP verhindert. Ein zweiter Aufruf von IoCompleteRequest bewirkt, dass der E/A-Manager den Aufruf der Abschlussroutinen des IRP fortsetzt, beginnend mit der Abschlussroutine direkt über der Routine, die STATUS_MORE_PROCESSING_REQUIRED zurückgegeben hat.
Wenn die IoCompletion-Routine einen eingehenden IRP wiederverwendet, um eine oder mehrere Anforderungen an niedrigere Treiber zu senden, oder wenn die Routine fehlgeschlagene Vorgänge wiederholt, sollte sie den Kontext aktualisieren, den die IoCompletion-Routine bei jeder Wiederverwendung oder Wiederholung des IRP verwaltet. Anschließend kann er den E/A-Stapelspeicherort des nächstniedrigen Treibers erneut einrichten, IoSetCompletionRoutine mit einem eigenen Einstiegspunkt aufrufen und IoCallDriver für das IRP aufrufen.
Die IoCompletion-Routine sollte ioMarkIrpPending nicht bei jeder Wiederverwendung oder bei jedem Wiederholungsversuch des IRP aufrufen.
Die Dispatchroutine hat den ursprünglichen IRP bereits als ausstehend markiert. Bis alle Treiber in der Kette die ursprüngliche IRP mit IoCompleteRequest abschließen, bleibt sie ausstehend.
Bevor eine Anforderung wiederholt wird, sollte die IoCompletion-Routine den E/A-status-Block mit STATUS_SUCCESS für Status und null für Information zurücksetzen, möglicherweise nach dem Speichern der zurückgegebenen Fehlerinformationen.
Für jeden Wiederholungsversuch dekrementiert die IoCompletion-Routine in der Regel eine Wiederholungsanzahl, die von der Dispatch-Routine eingerichtet wurde. In der Regel muss die IoCompletion-RoutineIoCompleteRequest aufrufen, um das IRP zu schlagen, wenn eine begrenzte Anzahl von Wiederholungen fehlgeschlagen ist.
Die IoCompletion-Routine muss STATUS_MORE_PROCESSING_REQUIRED zurückgeben, nachdem sie IoSetCompletionRoutine und IoCallDriver mit einem IRP aufgerufen hat, der wiederverwendet oder wiederholt wird.
Die Rückgabe von STATUS_MORE_PROCESSING_REQUIRED aus der IoCompletion-Routine verhindert die Abschlussverarbeitung eines wiederverwendeten oder wiederholten IRP durch den E/A-Manager.
Wenn die IoCompletion-Routine den ursprünglichen IRP mit STATUS_SUCCESS nicht abschließen kann, muss sie den E/A-status-Block wie von niedrigeren Treibern für den Wiederverwendungs- oder Wiederholungsvorgang zurückgegeben belassen, der dazu führt, dass die IoCompletion-Routine das IRP fehlschlägt.
Wenn die IoCompletion-Routine die ursprüngliche Anforderung mit STATUS_PENDING abschließt, muss ioMarkIrpPending mit dem ursprünglichen IRP aufgerufen werden, bevor IoCompleteRequest aufgerufen wird.
Wenn die IoCompletion-Routine den ursprünglichen IRP mit einem Fehler STATUS_XXX fehlschlagen muss, kann ein Fehler protokolliert werden. Es liegt jedoch in der Verantwortung des zugrunde liegenden Gerätetreibers, alle auftretenden Geräte-E/A-Fehler zu protokollieren, sodass IoCompletion-Routinen in der Regel keine Fehler protokollieren.
Jeder Treiber, der eine IoCompletion-Routine in einem IRP festlegt und den IRP dann an einen niedrigeren Treiber übergibt, sollte das IRP-PendingReturned-Flag in der IoCompletion-Routine> überprüfen. Wenn das Flag festgelegt ist, muss die IoCompletion-RoutineIoMarkIrpPending mit dem IRP aufrufen. Beachten Sie jedoch, dass ein Treiber, der das IRP übergibt und dann auf ein Ereignis wartet, das IRP nicht als ausstehend markieren sollte. Stattdessen sollte die IoCompletion-Routine das Ereignis signalisieren und STATUS_MORE_PROCESSING_REQUIRED zurückgeben.
Die IoCompletion-Routine muss alle Ressourcen freigeben, die die Dispatchroutine für die Verarbeitung des ursprünglichen IRP zugewiesen hat, vorzugsweise bevor die IoCompletion-Routine IoCompleteRequest mit dem ursprünglichen IRP aufruft und definitiv bevor die IoCompletion-Routine die Kontrolle nach abschluss des ursprünglichen IRP zurückgibt.
Wenn ein Treiber auf höherer Ebene seine IoCompletion-Routine im ursprünglichen IRP festgelegt hat, wird die IoCompletion-Routine dieses Treibers erst aufgerufen, wenn die IoCompletion-Routinen aller Treiber auf niedrigerer Ebene aufgerufen wurden.
Bereitstellen einer Prioritätserhöhung in Aufrufen von IoCompleteRequest
Wenn ein Gerätetreiber der niedrigsten Ebene ein IRP in seiner Dispatchroutine abschließen kann, ruft er IoCompleteRequest mit einem PriorityBoost von IO_NO_INCREMENT auf. Es ist keine Erhöhung der Laufzeitpriorität erforderlich, da der Treiber davon ausgehen kann, dass der ursprüngliche Anforderer nicht auf den Abschluss seines E/A-Vorgangs gewartet hat.
Andernfalls stellt der Treiber der niedrigsten Ebene einen systemdefinierten und gerätetypspezifischen Wert bereit, der die Laufzeitpriorität des Anforderers erhöht, um die Zeit zu kompensieren, die der Anforderer auf seine Geräte-E/A-Anforderung gewartet hat. Die Boostwerte finden Sie unter Wdm.h oder Ntddk.h.
Treiber auf höherer Ebene wenden beim Aufrufen von IoCompleteRequest den gleichen PriorityBoost wie ihre jeweiligen zugrunde liegenden Gerätetreiber an.
Auswirkung des Aufrufens von IoCompleteRequest
Wenn ein Treiber IoCompleteRequest aufruft, füllt der E/A-Manager den E/A-Stapelspeicherort des Treibers mit Nullen auf, bevor er ggf. den nächsthöheren Treiber aufruft, der eine IoCompletion-Routine eingerichtet hat, die für das IRP aufgerufen werden soll.
Die IoCompletion-Routine eines Treibers auf höherer Ebene kann nur den I/O-status-Block des IRP überprüfen, um zu bestimmen, wie alle niedrigeren Treiber die Anforderung verarbeitet haben.
Der Aufrufer von IoCompleteRequest darf nicht versuchen, auf die gerade abgeschlossene IRP zuzugreifen. Ein solcher Versuch ist ein Programmierfehler, der zu einem Systemabsturz führt.