6. Non-reentrancy of interrupt handlers
In the previous section, we mentioned that sometimes interrupts need to be masked, but why should we mask this interrupt? This is not because it is technically impossible to parallelize the same interrupt routine, but because of management considerations. The reason why new interrupts from the same IRQ must be blocked during interrupt processing is because the interrupt handler is not reentrant, so the same interrupt handler cannot be executed in parallel. Here we give an example. From this example, we can see that if an interrupt handler can be parallelized, then the driver lock is likely to occur. When the driver is locked, your operating system will not necessarily crash, but the device supported by the locked driver can no longer be used - when the device driver dies, the device dies as well.
The event that triggers PS1 will cause A1 to generate an interrupt, then B1 will read the existing data in R1, and then the code C1 will write data to R2. The event that triggers PS2 will cause A2 to generate an interrupt, then B2 deletes the data in R1, and then C2 reads the data in R2.
If PS1 is generated first, and when it is executed between A1 and B1, if PS2 is generated, A2 will generate an interrupt, interrupt PS2 (hang to the end of the task queue), and then delete R1 Content. When PS2 runs to C2, since C1 has not yet written data to R2, C2 will be suspended here, and PS2 sleeps on code C2 until it is awakened by a signal when data is readable. This is because the data in R1 that B2 in PS1 originally wanted to read was deleted by B2 in PS2, so the PS1 page will sleep on B1 until it is awakened by a signal when there is data to read. In this way, the event that wakes up PS1 and PS2 will never happen, so PS1 and PS2 are locked.
Since the device driver has to deal with the device registers, it is difficult to write reentrant code because the device registers are global variables. Therefore, the simplest way is to prohibit parallelism of interrupt handlers of the same device, that is, the interrupt handler of the device is not reentrant.
One thing must be clear: in the Linux kernel after version 2.0, all the upper halves are uninterruptible (the operations of the upper halves are atomic); the lower halves of different devices can interrupt each other , but a specific lower half cannot be interrupted by itself (i.e. the same lower half cannot be parallelized).
Since the interrupt handler is required to be non-reentrant, programmers don’t have to worry about writing reentrant code. In my experience, writing reentrant device drivers is possible, but writing reentrant interrupt handlers is very rare and almost impossible.
7. Avoid the occurrence of race conditions
We all know that once a race condition occurs, a deadlock may occur, and in severe cases, the entire system may be locked. So be sure to avoid race conditions. I won’t say much here, but everyone just needs to pay attention to one thing: most race conditions caused by interrupts are caused by the kernel process with interrupts being put to sleep. Therefore, when implementing interrupts, you must be careful to let the process sleep. If necessary, you can use cli, sti or save_flag, restore_flag. Please refer to the reference book specified in this article for specific details.
8. Implementation
How to implement the driver's interrupt routine is a matter for the readers. As long as you carefully read the source code of the short routine and understand the rules for writing driver interrupt routines, you can write your own interrupt routine. As long as the concepts are correct and your code is written under the correct rules, it makes sense. I always emphasize that concepts come first, and how much code can be written is secondary. We must have correct concepts in order to think correctly.
(T114)
The above is the in-depth analysis of Linux device driver interrupts (1) (3). For more related content, please pay attention to the PHP Chinese website (m.sbmmt.com)!