Synchronisieren von Interruptcode
Die folgenden Faktoren erschweren den Treibercode, der Hardwareunterbrechungen auf Multiprozessorsystemen verarbeitet:
Jedes Mal, wenn ein Gerät unterbrochen wird, stellt es interruptspezifische Informationen bereit, die flüchtig sind, da es bei der nächsten Unterbrechung des Geräts überschrieben werden kann.
Geräte unterbrechen bei relativ hohen IRQLs, und ihre Interruptdienstroutinen (ISRs) können die Ausführung anderer Treibercodes unterbrechen.
Bei DIRQL-Interrupts muss die ISR bei DIRQL ausgeführt werden, während eine vom Treiber bereitgestellte Drehsperre beibehalten wird, damit die ISR zusätzliche Unterbrechungen verhindern kann, während flüchtige Informationen gespeichert werden. Der DIRQL verhindert eine Unterbrechung durch den aktuellen Prozessor, und die Drehsperre verhindert die Unterbrechung durch einen anderen Prozessor.
Die ISR muss schnell ausgeführt werden, da das Gerät während der Ausführung der ISR nicht unterbrechen kann. Lange ISR-Ausführungszeiten können das System verlangsamen oder möglicherweise zu Datenverlust führen.
Sowohl die ISR- als auch die DPC-Routine (Verzögerter Prozeduraufruf) müssen in der Regel auf einen Speicherbereich zugreifen, in dem der ISR die flüchtigen Daten des Geräts speichert. Diese Routinen müssen miteinander synchronisiert werden, damit sie nicht gleichzeitig auf den Speicherbereich zugreifen.
Aufgrund all dieser Faktoren müssen Sie beim Schreiben von Treibercode, der Interrupts behandelt, die folgenden Regeln verwenden:
Nur die EvtInterruptIsr-Rückruffunktion greift auf flüchtige Interruptdaten zu, z. B. Geräteregister, die Interruptinformationen enthalten.
Die EvtInterruptIsr-Rückruffunktion sollte die flüchtigen Daten in einen treiberdefinierten Interruptdatenpuffer verschieben, auf den die EvtInterruptDpc-Rückruffunktion des Treibers, die EvtInterruptWorkItem-Rückruffunktion oder mehrere EvtDpcFunc-Rückruffunktionen zugreifen können.
Wenn Ihr Treiber EvtInterruptDpc - oder EvtInterruptWorkItem-Rückruffunktionen für seine Interruptobjekte bereitstellt, ist der beste Ort zum Speichern von Interruptdaten der Kontextbereich des Interruptobjekts. Die Rückruffunktionen des Interruptobjekts können mithilfe des empfangenen Objekthandles auf den Kontextbereich des Objekts zugreifen.
Wenn Ihr Treiber mehrere EvtDpcFunc-Rückruffunktionen für jede EvtInterruptIsr-Rückruffunktion bereitstellt, können Sie Interruptdaten im Kontextbereich jedes DPC-Objekts speichern.
Der gesamte Treibercode, der auf den Interruptdatenpuffer zugreift, muss synchronisiert werden, sodass jeweils nur eine Routine auf die Daten zugreift.
Bei DIRQL-Interruptobjekten greift die EvtInterruptIsr-Rückruffunktion unter IRQL = DIRQL auf diesen Datenpuffer zu, während die vom Treiber bereitgestellte Spinsperre des Interruptobjekts beibehalten wird. Daher müssen alle Routinen, die auf den Puffer zugreifen, auch bei DIRQL ausgeführt werden, während die Drehsperre gedrückt wird. (In der Regel ist die Rückruffunktion EvtInterruptDpc oder EvtDpcFunc des Interrupts die einzige andere Routine, die auf den Puffer zugreifen muss.)
Alle Routinen, die auf einen Interruptdatenpuffer zugreifen, mit Ausnahme der EvtInterruptIsr-Rückruffunktion , müssen eine der folgenden Aktionen ausführen:
- Rufen Sie WdfInterruptSynchronize auf, um eine EvtInterruptSynchronize-Rückruffunktion zu planen, die auf den Interruptdatenpuffer zugreift.
- Platzieren Sie Code, der auf den Interruptdatenpuffer zwischen Aufrufen von WdfInterruptAcquireLock und WdfInterruptReleaseLock zugreift.
Beide Techniken ermöglichen es der EvtInterruptDpc - oder EvtDpcFunc-Funktion , bei DIRQL auf Interruptdaten zuzugreifen, während die Drehsperre des Interrupts gedrückt bleibt. Der DIRQL verhindert eine Unterbrechung durch den aktuellen Prozessor, und die Drehsperre verhindert die Unterbrechung durch einen anderen Prozessor.
Wenn Ihr Gerät mehrere Interruptvektoren oder -nachrichten unterstützt und Sie die Behandlung dieser Interrupts durch Ihren Treiber synchronisieren möchten, können Sie mehreren DIRQL-Interruptobjekten eine einzelne Drehsperre zuweisen. Das Framework bestimmt die höchste DIRQL der Gruppe von Interrupts und ruft immer die Drehsperre an diesem DIRQL ab, sodass der synchronisierte Code nicht durch Interruptvektoren oder Meldungen in der Gruppe unterbrochen werden kann.
Bei Interruptobjekten auf passiver Ebene ruft das Framework die Interruptsperre auf passiver Ebene ab, bevor die EvtInterruptIsr-Rückruffunktion des Treibers unter IRQL = PASSIVE_LEVEL aufgerufen wird. Daher müssen alle Routinen, die auf den Puffer zugreifen, entweder die Interruptsperre abrufen oder den Pufferzugriff intern synchronisieren. In der Regel ist die EvtInterruptWorkItem-Rückruffunktion des Interrupts die einzige andere Routine, die auf den Puffer zugreift. Informationen zum Abrufen der Interruptsperre von einer EvtInterruptWorkItem-Rückruffunktion finden Sie im Abschnitt Hinweise auf dieser Seite.
Sie können auch die Behandlung mehrerer Interruptvektoren durch den Treiber synchronisieren, indem Sie mehreren Interruptobjekten auf passiver Ebene eine einzelne Wartesperre zuweisen.
Wenn ein Teil Ihres Codes, der DIRQL-Interrupts verarbeitet, unter IRQL = PASSIVE_LEVEL ausgeführt werden muss, kann Ihre EvtInterruptDpc - oder EvtDpcFunc-Rückruffunktion ein oder mehrere Arbeitselemente erstellen, sodass der Code als EvtWorkItem-Rückruffunktionen ausgeführt wird.
Alternativ kann der Treiber in KMDF-Versionen 1.11 und höher ein Interruptarbeitselement anfordern, indem er WdfInterruptQueueWorkItemForIsr aufruft. (Denken Sie daran, dass die EvtInterruptIsr-Rückruffunktion eines Treibers WdfInterruptQueueWorkItemForIsr oder WdfInterruptQueueDpcForIsr aufrufen kann, aber nicht beide.)
Wenn es wichtig ist, die Rückruffunktionen EvtInterruptDpc und EvtDpcFunc eines Treibers untereinander und mit anderen Rückruffunktionen zu synchronisieren, die einem Gerät zugeordnet sind, kann Ihr Treiber den AutomaticSerialization-Member in der WDF_INTERRUPT_CONFIG-Struktur des Interrupts und der WDF_DPC_CONFIG-Struktur des DPC-Objekts auf TRUE festlegen. Alternativ kann der Treiber Framework-Spinsperren verwenden. (Wenn Sie das Element AutomaticSerialization auf TRUE festlegen, wird eine EvtInterruptIsr-Rückruffunktion nicht mit anderen Rückruffunktionen synchronisiert. Verwenden Sie WdfInterruptSynchronize oder WdfInterruptAcquireLock , um eine EvtInterruptIsr-Rückruffunktion zu synchronisieren, wie zuvor in diesem Thema beschrieben.)
Weitere Informationen zum Synchronisieren von Treiberroutinen finden Sie unter Synchronisierungstechniken für Framework-Based Treiber.