Transferred from: http://blog.csdn.net/maray/article/details/5770889
Cannot sleep in the Linux kernel interrupt path, why?
This is a very in-depth discussion, worth a look: http://bbs2.chinaunix.net/viewthread.php?tid=1618430
However, their discussions did not come to a definitive conclusion at the end. Among them,Cskyrain on the 8 floor of the thinking touches a point, but not in-depth expansion:
[C-sharp]View PlainCopy
- 1 floor posted on 2009-11-24 20:36 | Just look at the author
- Always think that interrupt processing function can not sleep is the natural, but never seriously think about what can not sleep, blocking. Recently looked at the Ulk in the explanation of this, feeling still a bit not quite understand,
- "The price to pay for allowing nested kernel control paths is that an interrupt Handler must never block, that is, no process < span class= "keyword" >switch can take place until an interrupt handler < span class= "keyword" >is running. in fact, all the data needed to Resume a nested kernel control path is stored < span class= "keyword" >in the kernel mode stack, which is tightly bound to the current process. "
- Above the interrupt handler cannot sleep boils down to the interrupt handler can be nested, and the recovery of the nested interrupt handler related data placed in the kernel stack, this stack and the current process associated, here is not clear, since there is a stack of stored data, and after the process has been switched out, the stack will not be destroyed, When the process is cut back, can not the same nested interrupt handler return? (This does not test the interrupt processing time is too long, affecting the service problem of interrupt processing, only indicates whether the interruption can hibernate). At the same time I Google, see someone on the interruption can not sleep with an explanation:
- ”
- All data used by the interrupt handler is stored in the kernel stack of the current process (typically), if this occurs
- Process switching, the interrupt handler will be blocked, and when a process switch occurs at one time, it may not be immediately swapped back. Like when
- When a keyboard interrupt occurs, the keyboard handler is in progress, and then a process preemption occurs, switching to another process
- , if the process does not cause the kernel to stabilize, then the interrupt handler will be blocked, and the inner wo can not respond to the interrupt at all,
- Until a process switchover occurs at one time. Assuming that the end of the process is switched back to the one executing the interrupt handler, if its kernel stack
- What if you have been unintentionally damaged? The interrupt handler may not be able to continue running, and the interrupt service fails. And
- Now the handlers are allowed to interrupt nesting, one interrupt is blocked, and the others will be blocked. So in the face of intricate kernel logic,
- The best approach is to disable process switching in the interrupt handler, which improves the response speed of the interrupt handler and increases the
- Stability and security of the kernel.
- “
- I feel he has two reasons: one thing is efficiency, it can affect the speed of processing, and the other is: if its kernel stack
- What if you have been unintentionally damaged?
- The first reason is not discussed,
- But the second, the reason I feel very far-fetched, if the stack is so easy to destroy, I can also say that describes the process of data structure and so may be destroyed, which is not the process scheduling is not safe,
- So according to him, there is only the first reason for efficiency.
- Back to Ulk's explanation, I don't know if it's a problem I understand, the price-to-pay for allowing nested kernel control paths was that an interrupt handler must Never block
- It feels like he has attributed the inability of the midrange to hibernation to support the nesting of interrupt handlers. That is not to understand, if not support interrupt nesting, interrupt handler can sleep, hehe, it seems that this is not right,
- Question: What is the reason why the interrupt handler cannot hibernate?
7 Floor:
[C-sharp]View PlainCopy
- Kouu published on 2009-11-25 11:52
- Reply to a post #6 Cskyrain
- Interrupts cannot block, especially asynchronous interrupts.
- Look at LZ these two days of the post, I think it is similar to the 4 floor of the statement is more reliable: asynchronous interrupts are independent of the context, not related to the current process, so can not interrupt the context of the block and the innocent process to block. It might be based on the idea that real-time Linux, which has threaded the interrupt handlers, should allow the block to be interrupted.
- Synchronous interrupts (such as system calls, page faults) are representative of the current process, which can be block.
8 Floor:
[C-sharp]View PlainCopy
- Reply to a post #7 kouu
- Oh, and turn over the book, think about it, because of the time problem, no mood to read from the beginning Ulk, just jump to check a bit, will inevitably miss a lot of things, but still say my understanding:
- Here interrupts represent only asynchronous interrupts, and exceptions represent synchronous interrupts, so system calls are exception handling, not interrupt processing.
- Exception handling here can sleep block, because the data required for exception handling is stored in the exception stack, and each process has an exception stack, so exception handling and process is associated, so that exception handling can block, be dispatched.
- For interrupts, there are two situations, one is to interrupt the use of a separate interrupt stack instead of the kernel stack of the process, so that because all interrupts share an interrupt stack, the interrupt stack is not associated with a particular process, so this interrupt can not block, after the block he can no longer be dispatched.
- The second case is that the interrupt does not use a separate interrupt stack, but instead uses the current process's kernel stack, which I think is the same as the exception handling, this interrupt can block, the reason is not allowed to interrupt the block is not technically switched back, But logically to improve the efficiency of processing force it can not block.
- The above is my understanding at this stage, I feel that the first two explanations should be no problem, but the end of the interrupt is not to use interrupt stack interpretation may have a wrong understanding, I do not know kouu to this explanation?
Overall, the initial conclusion is that the kernel can not sleep in the interrupt path, not technically, but there is no reason to do so, or in the interrupt path to sleep unreasonable. On the one hand, the external event causes the current process time slice to be deprived, unreasonable; on the one hand, the interrupt service program should deal with interrupts as soon as possible to ensure the IO throughput rate.
-------------------------------Split Line----------------------------
By reading the third edition of the Ulk Chinese version of page 164, let me have a clearer understanding of this issue:
When the kernel is compiled with the value of thread_size set to 8K, then the kernel stack of each process is 8K, and if an interrupt occurs, then the register equivalent of the process will be saved to its 8K kernel stack. However, if the size of the thread_size is set to 4K, the kernel will use 3 types of kernel stacks, exception stacks, hardware interrupt request stacks, and soft interrupt request stacks (when using 4K stacks, interrupts get their own stack ins Tead of using the currently active kernel stack.)
* Exception stack: one per process.
* Hard Interrupt Request stack: One per CPU, each occupying a separate page box. The DO_IRQ () function is responsible for switching to the stack internally by calling Execute_on_irq_stack.
* Soft Interrupt Request stack: One per CPU, each occupying a separate page box.
For a way to switch to the interrupt stack, take a look at the following code:
[CPP]View PlainCopy
- /*
- * DO_IRQ handles all normal device IRQ ' s (The Special
- * SMP CROSS-CPU interrupts have their own specific
- * handlers).
- */
- unsigned int do_irq (struct pt_regs *regs)
- {
- struct Pt_regs *old_regs;
- / * High bit used in RET_FROM_ code * /
- int overflow;
- unsigned vector = ~regs->orig_ax;
- struct IRQ_DESC *desc;
- unsigned IRQ;
- Old_regs = Set_irq_regs (regs);
- Irq_enter ();
- IRQ = __get_cpu_var (VECTOR_IRQ) [vector];
- overflow = Check_stack_overflow ();
- desc = Irq_to_desc (IRQ);
- if (unlikely (!desc)) {
- PRINTK (Kern_emerg "%s:cannot handle IRQ%d vector% #x CPU%d/n",
- __func__, IRQ, Vector, smp_processor_id ());
- BUG ();
- }
- if (!execute_on_irq_stack (overflow, desc, IRQ)) {
- if (unlikely (overflow))
- Print_stack_overflow ();
- DESC->HANDLE_IRQ (IRQ, DESC);
- }
- Irq_exit ();
- Set_irq_regs (Old_regs);
- return 1;
- }
[CPP]View PlainCopy
- static inline int
- Execute_on_irq_stack (int overflow, struct irq_desc *desc, int IRQ)
- {
- Union Irq_ctx *curctx, *irqctx;
- U32 *isp, arg1, arg2;
- Curctx = (Union irq_ctx *) Current_thread_info ();
- Irqctx = hardirq_ctx[smp_processor_id ()];
- /*
- * This is where we switch to the IRQ stack. However, if we are
- * Already using the IRQ stack (because we interrupted a HARDIRQ
- * Handler) We can ' t do and just has to keep using the
- * Current stack (which are the IRQ stack already after all)
- */
- if (unlikely (curctx = = irqctx))
- return 0;
- / * Build the stack frame on the IRQ stack * /
- ISP = (U32 *) ((char*) Irqctx + sizeof (*IRQCTX));
- Irqctx->tinfo.task = curctx->tinfo.task;
- Irqctx->tinfo.previous_esp = Current_stack_pointer;
- /*
- * Copy the SOFTIRQ bits in Preempt_count so, the
- * SOFTIRQ checks work in the HARDIRQ context.
- */
- Irqctx->tinfo.preempt_count =
- (Irqctx->tinfo.preempt_count & ~softirq_mask) |
- (Curctx->tinfo.preempt_count & Softirq_mask);
- if (unlikely (overflow))
- Call_on_stack (Print_stack_overflow, ISP);
- ASM volatile ("Xchgl%%ebx,%%esp/n"
- "Call *%%edi/n"
- "Movl%%ebx,%%esp/n"
- : "=a" (arg1), "=d" (arg2), "=b" (ISP)
- : "0" (IRQ), "1" (DESC), "2" (ISP),
- "D" (DESC->HANDLE_IRQ)
- : "Memory", "CC", "ecx");
- return 1;
- }
Finally, consider the question in the title:why the Linux kernel cannot sleep/dispatch within the interrupt path
Linux is a process-based scheduling unit, and the scheduler only sees the process kernel stack, and the interrupt stack is not visible. In the standalone interrupt stack mode, if the Linux kernel is dispatched within the interrupt path (technically, sleep and dispatch are meant to be), then Linux will not be able to find the "way Home", and the execution opportunity will no longer be available for execution of the interrupt handling code.
Re-think of the reason why the Linux kernel cannot sleep/dispatch within the Interrupt path (2010) "Go"