Synchronizowanie kodu przerwania
Następujące czynniki komplikują kod sterownika, który obsługuje przerwania sprzętowe w systemach wieloprocesorowych:
Za każdym razem, gdy urządzenie przerywa, dostarcza informacje specyficzne dla przerwań, które są niestabilne, ponieważ można je zastąpić przy następnym przerwaniu działania urządzenia.
Urządzenia przerywają na relatywnie wysokich poziomach IRQL, a ich procedury obsługi przerwań (ISR) mogą przerywać wykonywanie innego kodu sterownika.
W przypadku przerwań DIRQL, ISR musi pracować na poziomie DIRQL przy utrzymywaniu blokady spin dostarczonej przez sterownik, aby ISR mógł zapobiec dodatkowym przerwaniom podczas zapisywania niestabilnych danych. DiRQL zapobiega przerwom w działaniu bieżącego procesora, a blokada spinu zapobiega przerwom w działaniu innego procesora.
ISR musi działać szybko, ponieważ urządzenie nie może zostać przerwane, gdy ISR jest wykonywane. Długie czasy wykonywania ISR mogą spowolnić system lub spowodować utratę danych.
Zarówno ISR, jak i procedura odroczona (DPC) muszą zwykle uzyskiwać dostęp do obszaru magazynu, w którym ISR przechowuje ulotne dane urządzenia. Te procedury muszą być synchronizowane ze sobą, aby nie miały jednocześnie dostępu do obszaru magazynu.
Ze względu na wszystkie te czynniki należy użyć następujących reguł podczas pisania kodu sterownika, który obsługuje przerwania:
Tylko funkcja wywołania zwrotnego EvtInterruptIsr uzyskuje dostęp do ulotnych danych przerwań, takich jak rejestry urządzeń zawierające informacje o przerwaniu.
Funkcja wywołania zwrotnego EvtInterruptIsr powinna przenieść dane nietrwałe do buforu danych przerwania zdefiniowanego przez sterownik, do którego może uzyskać dostęp funkcja wywołania zwrotnego EvtInterruptDpc, funkcja wywołania zwrotnego EvtInterruptWorkItem lub wiele funkcji wywołania zwrotnego EvtDpcFunc.
Jeśli sterownik udostępnia funkcje wywołania zwrotnego EvtInterruptDpc lub EvtInterruptWorkItem dla obiektów przerwania, najlepszym miejscem do przechowywania danych przerwań jest w przestrzeni kontekstowej obiektu przerwania . Funkcje wywołania zwrotnego obiektu przerwania mogą uzyskiwać dostęp do przestrzeni kontekstowej obiektu przy użyciu odbieranego przez nie uchwytu obiektu.
Jeśli sterownik udostępnia wiele EvtDpcFunc funkcji wywołania zwrotnego dla każdej funkcji wywołania zwrotnego EvtInterruptIsr, możesz przechowywać dane przerwań w przestrzeni kontekstowej każdego obiektu DPC.
Cały kod sterownika, który uzyskuje dostęp do buforu danych przerwania, musi być zsynchronizowany, tak aby tylko jedna procedura uzyskiwała dostęp do danych jednocześnie.
W przypadku obiektów przerwań DIRQL funkcja wywołania zwrotnego EvtInterruptIsr uzyskuje dostęp do tego bufora danych w IRQL = DIRQL, podczas trzymania blokady spinlock dostarczonej przez sterownik obiektu przerwania. W związku z tym wszystkie procedury, które uzyskują dostęp do buforu, muszą również działać w DIRQL, trzymając blokadę spinową. (Zazwyczaj EvtInterruptDpc lub EvtDpcFunc funkcja wywołania zwrotnego jest jedyną inną procedurą, która musi uzyskać dostęp do buforu).
Wszystkie procedury, które uzyskują dostęp do buforu danych przerwań, z wyjątkiem EvtInterruptIsr funkcji wywołania zwrotnego, muszą wykonać jedną z następujących czynności:
- Wywołaj WdfInterruptSynchronize, aby zaplanować funkcję wywołania zwrotnego EvtInterruptSynchronize, która uzyska dostęp do buforu danych przerwania.
- Umieść kod, który uzyskuje dostęp do buforu danych przerwania między wywołaniami WdfInterruptAcquireLock i WdfInterruptReleaseLock.
Obie te techniki umożliwiają funkcji EvtInterruptDpc lub EvtDpcFunc na uzyskanie dostępu do danych przerwania w DIRQL przy trzymaniu blokady spinowej przerwania. DiRQL zapobiega przerwom w działaniu bieżącego procesora, a blokada spinu zapobiega przerwom w działaniu innego procesora.
Jeśli urządzenie obsługuje wiele wektorów przerwań lub komunikatów, a jeśli chcesz zsynchronizować obsługę tych przerwań sterownika, możesz przypisać pojedynczą blokadę spin do wielu obiektów przerwań na poziomie DIRQL. Struktura określa najwyższy poziom DIRQL zestawu przerwań i zawsze uzyskuje blokadę obrotową na tym poziomie DIRQL, aby zsynchronizowany kod nie mógł zostać przerwany przez wektory przerwań lub komunikaty w zestawie.
W przypadku obiektów przerwań na poziomie pasywnym , struktura uzyskuje blokadę przerwań na poziomie pasywnym przed wywołaniem EvtInterruptIsr funkcji wywołania zwrotnego w IRQL = PASSIVE_LEVEL. W związku z tym wszystkie procedury, które uzyskują dostęp do buforu, muszą uzyskać blokadę przerwania lub wewnętrznie zsynchronizować dostęp buforu. Zazwyczaj funkcja wywołania zwrotnego EvtInterruptWorkItem jest jedyną inną procedurą, która uzyskuje dostęp do buforu. Aby uzyskać informacje na temat uzyskiwania blokady przerwania z EvtInterruptWorkItem funkcji wywołania zwrotnego, zobacz sekcję Uwagi tej strony.
Można również zsynchronizować obsługę wielu wektorów przerwań przez sterownik, przypisując pojedynczą blokadę oczekiwania do wielu obiektów przerwań na poziomie pasywnym.
Jeśli część twojego kodu obsługującego przerwania DIRQL musi działać na poziomie IRQL = PASSIVE_LEVEL, funkcja wywołania zwrotnego EvtInterruptDpc lub EvtDpcFunc może utworzyć jeden lub więcej elementów roboczych, aby kod mógł działać jako funkcje wywołania zwrotnego EvtWorkItem.
Alternatywnie w usługach KMDF w wersji 1.11 lub nowszej sterownik może zażądać przerwania elementu roboczego, wywołując WdfInterruptQueueWorkItemForIsr. Przypomnij sobie, że EvtInterruptIsr funkcji wywołania zwrotnego może wywołać funkcję WdfInterruptQueueWorkItemForIsr lub WdfInterruptQueueDpcForIsr, ale nie obu naraz.
Jeśli ważne jest synchronizowanie funkcji wywołania zwrotnego EvtInterruptDpc i EvtDpcFunc między sobą oraz z innymi funkcjami wywołania zwrotnego skojarzonymi z urządzeniem, sterownik może ustawić członka AutomaticSerialization na TRUE w strukturze WDF_INTERRUPT_CONFIG przerwania oraz w strukturze obiektu DPC WDF_DPC_CONFIG. Alternatywnie sterownik może używać struktury spin locks. (Ustawienie elementu członkowskiego AutomaticSerialization na TRUE nie synchronizuje funkcji wywołania zwrotnego EvtInterruptIsr z innymi funkcjami wywołania zwrotnego. Użyj WdfInterruptSynchronize lub WdfInterruptAcquireLock, aby zsynchronizować funkcję wywołania zwrotnego EvtInterruptIsr, jak opisano wcześniej w tym temacie.)
Aby uzyskać więcej informacji na temat synchronizowania procedur sterowników, zobacz Synchronization Techniques for Framework-Based Drivers.