Synchronizing Multiple Interrupts.

0 Likes

Problem:

I have some hardware that I wrote a driver for.

I can handle multiple interrupts and then access a common buffer.

Since interrupts run at a high priority how do I keep one interrupt from corrupting data used by the other?

Resolution:

Synchronizing Multiple Interrupts

The Windows NT kernel provides a mechanism to protect data that is shared between an interrupt service routine (ISR) and code running at a lower priority level. If this mechanism were not available, then the ISR could execute while the lower priority code was in the process of modifying the shared data, potentially resulting in a catastrophic failure.

The mechanism that the system provides is the service KeSynchronizeExecution:

BOOLEAN KeSynchronizeExecution(

IN PKINTERRUPT Interrupt,

IN PKSYNCHRONIZEROUTINE SynchronizeRoutine,

IN PVOID SynchronizeContext

);

A driver calls KeSynchronizeExecution when it needs to access data shared with the ISR. The parameter SynchronizeRoutine is the address of a function that actually accesses the shared data. The service calls the supplied function at raised IRQL, while holding the spin lock associated with the interrupt. If an interrupt occurs while SynchronizeRoutine is running, the execution of the ISR is deferred until SynchronizeRoutine returns.

Now suppose the driver is managing not one, but possibly several interrupts. Each interrupt has its own priority level (IRQL). If the driver writer does not take precautions, then one ISR could interrupt the execution of a second ISR, raising the possibility of data corruption.

One way to avoid the problem is to first determine which interrupt has the highest IRQL, and use KeSynchronizeExecution from inside the ISRs of interrupts that have lower IRQLs. The ISR for the lower priority interrupt passes in a pointer to the interrupt object having the highest IRQL. The system takes the spin lock associated with the higher level interrupt, thereby ensuring atomic access to the critical data. This is just a generalization of the usage of KeSynchronizeExecution as described for sharing data between ISRs and non-interrupt code.

Because it could be expensive to make extra calls and to change IRQLs while in an ISR, NT enables the driver writer to assign a common synchronization level for a set of interrupts. For a set of interrupts that have a common IRQL, the associated ISRs cannot interrupt one another, so they don't have to call KeSynchronizeExecution to ensure atomic access to data. The key to setting a common synchronization level for a set of interrupts is found in the service IoConnectInterrupt:

NTSTATUS IoConnectInterrupt(

OUT PKINTERRUPT *InterruptObject,

IN PKSERVICE_ROUTINE ServiceRoutine,

IN PVOID ServiceContext,

IN PKSPIN_LOCK SpinLock,

IN ULONG Vector,

IN KIRQL Irql,

IN KIRQL SynchronizeIrql,

IN KINTERRUPT_MODE InterruptMode,

IN BOOLEAN ShareVector,

IN KAFFINITY ProcessorEnableMask,

IN BOOLEAN FloatingSave

);

The parameter in question is SynchronizeIrql. To determine this value, the driver first collects the IRQLs for all of its interrupts (using HalGetInterruptVector), and selects the greatest. The driver must also initialize storage for a spin lock, and pass its address in parameter SpinLock. The spin lock must remain in existence while the interrupts are hooked.

Once the driver has determined the greatest IRQL for all its interrupts, and has intialized a spin lock, it calls IoConnectInterrupt for each interrupt, using the same values for SynchronizeIrql and SpinLock for each call. A driver that uses this technique causes all of its ISRs to run at the same IRQL, which greatly simplifies the task of serializing access to critical data.

If you develop NT or WDM drivers with DriverWorks™, it is very easy to use this method. The class that handles interrupts is called KInterrupt. There are two forms of KInterrupt::Connect. The first form is the simple form, used when a driver has only one interrupt, or when multiple interrupts do not require synchronization:

NTSTATUS KInterrupt::Connect(PKSERVICE_ROUTINE Isr, PVOID Context);

The second form is for the case when there are multiple interrupts requiring synchronization:

NTSTATUS KInterrupt::Connect(

PKSERVICE_ROUTINE Isr,

PVOID Context,

PKSPIN_LOCK pSpin,

KIRQL SynchIrql

);

Old KB# 12118
Comment List
Anonymous
Related Discussions
Recommended