分类: LINUX
2006-08-04 17:24:29
401 /* 402 * Build the entry stubs and pointer table with 403 * some assembler magic. 404 */ 405 .data 406 ENTRY(interrupt) 407 .text 408 409 vector=0 410 ENTRY(irq_entries_start) 411 .rept NR_IRQS 412 ALIGN 413 1: pushl $vector-256 414 jmp common_interrupt 415 .data 416 .long 1b 417 .text 418 vector=vector+1 419 .endr 420 421 ALIGN 422 common_interrupt: 423 SAVE_ALL 424 movl %esp,%eax 保存现场 425 call do_IRQ 调用中断处理函数 |
49 /* 50 * do_IRQ handles all normal device IRQ's (the special 51 * SMP cross-CPU interrupts have their own specific 52 * handlers). 53 */ 54 fastcall unsigned int do_IRQ(struct pt_regs *regs) 55 { 56 /* high bits used in ret_from_ code */ 57 int irq = regs->orig_eax & 0xff; 58 #ifdef CONFIG_4KSTACKS 59 union irq_ctx *curctx, *irqctx; 60 u32 *isp; 61 #endif 62 63 irq_enter(); |
101 #define irq_enter() \ 102 do { \ 103 account_system_vtime(current); \ 统计系统时间 104 add_preempt_count(HARDIRQ_OFFSET); \ 将抢占计数器加上HARDIRQ_OFFSET,避免抢占 105 } while (0) |
64 #ifdef CONFIG_DEBUG_STACKOVERFLOW 65 /* Debugging check for stack overflow: is there less than 1KB free? */ 66 { 67 long esp; 68 69 __asm__ __volatile__("andl %%esp,%0" : 70 "=r" (esp) : "0" (THREAD_SIZE - 1)); 71 if (unlikely(esp < (sizeof(struct thread_info) + STACK_WARN))) { 72 printk("do_IRQ: stack overflow: %ld\n", 73 esp - sizeof(struct thread_info)); 74 dump_stack(); 75 } 76 } 77 #endif 78 79 #ifdef CONFIG_4KSTACKS 80 81 curctx = (union irq_ctx *) current_thread_info(); 82 irqctx = hardirq_ctx[smp_processor_id()]; 83 84 /* 85 * this is where we switch to the IRQ stack. However, if we are 86 * already using the IRQ stack (because we interrupted a hardirq 87 * handler) we can't do that and just have to keep using the 88 * current stack (which is the irq stack already after all) 89 */ 90 if (curctx != irqctx) { 91 int arg1, arg2, ebx; 92 93 /* build the stack frame on the IRQ stack */ 94 isp = (u32*) ((char*)irqctx + sizeof(*irqctx)); 95 irqctx->tinfo.task = curctx->tinfo.task; 96 irqctx->tinfo.previous_esp = current_stack_pointer; 97 98 asm volatile( 99 " xchgl %%ebx,%%esp \n" 100 " call __do_IRQ \n" 101 " movl %%ebx,%%esp \n" 102 : "=a" (arg1), "=d" (arg2), "=b" (ebx) 103 : "0" (irq), "1" (regs), "2" (isp) 104 : "memory", "cc", "ecx" 105 ); 106 } else 107 #endif 108 __do_IRQ(irq, regs); 为了简答起见,不分析DEBUG相关代码和4k堆栈的代码 |
102 /* 103 * do_IRQ handles all normal device IRQ's (the special 104 * SMP cross-CPU interrupts have their own specific 105 * handlers). 106 */ 107 fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs) 108 { 109 irq_desc_t *desc = irq_desc + irq; 110 struct irqaction * action; 111 unsigned int status; 112 113 kstat_this_cpu.irqs[irq]++; 114 if (CHECK_IRQ_PER_CPU(desc->status)) { 115 irqreturn_t action_ret; 116 117 /* 118 * No locking required for CPU-local interrupts: 119 */ 120 if (desc->handler->ack) 121 desc->handler->ack(irq); 122 action_ret = handle_IRQ_event(irq, regs, desc->action); 123 desc->handler->end(irq); 124 return 1; 125 } 如果有针对某个特定CPU的中断,则直接调用相应的中断处理函数。 126 127 spin_lock(&desc->lock); 因为在SMP系统上,可能有另外的CPU响应同一个中断源的中断,所以在访问此变量时需要自旋锁 128 if (desc->handler->ack) 129 desc->handler->ack(irq); 如果此中断需要回应,则调用回应处理函数 130 /* 131 * REPLAY is when Linux resends an IRQ that was dropped earlier 132 * WAITING is used by probe to mark irqs that are being tested 133 */ 134 status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); 135 status |= IRQ_PENDING; /* we _want_ to handle it */ 设置status的IRQ_PENDING位表示当前处理函数要处理它 136 137 /* 138 * If the IRQ is disabled for whatever reason, we cannot 139 * use the action we have. 140 */ 141 action = NULL; 142 if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { 143 action = desc->action; 144 status &= ~IRQ_PENDING; /* we commit to handling */ 145 status |= IRQ_INPROGRESS; /* we are handling it */ 如果这个中断处理函数并没有正在执行,或者被禁止,则将status状态更新到IRQ_INPROGRESS,表示我们正在处理此次中断 146 } 147 desc->status = status; 更新当前中断描述符的状态 148 149 /* 150 * If there is no IRQ handler or it was disabled, exit early. 151 * Since we set PENDING, if another processor is handling 152 * a different instance of this same irq, the other processor 153 * will take care of it. 154 */ 155 if (unlikely(!action)) 156 goto out; 如果这个中断没有处理函数,或者是中断处理被禁止了,或者是已经有一个此中断源引发的响应,则直接退出。 注意:如果此中断并不是没有中断处理函数,它的中断描述符的状态的IRQ_PENDING位将被设置,那么在上次中断返回之后,如果发现在它响应中断期间,又有新的中断发生,则直接响应这个嵌套了的中断。 157 158 /* 159 * Edge triggered interrupts need to remember 160 * pending events. 161 * This applies to any hw interrupts that allow a second 162 * instance of the same irq to arrive while we are in do_IRQ 163 * or in the handler. But the code here only handles the _second_ 164 * instance of the irq, not the third or fourth. So it is mostly 165 * useful for irq hardware that does not mask cleanly in an 166 * SMP environment. 167 */ 168 for (;;) { 169 irqreturn_t action_ret; 170 171 spin_unlock(&desc->lock); 与127行呼应,释放自旋锁。在SMP系统中,如果有一个中断阻塞在127行,那么此锁释放掉后,那个CPU就可以设置这个中断描述符的IRQ_PENDING位,这个CPU就可以真正响应这个中断了。 172 173 action_ret = handle_IRQ_event(irq, regs, action); 174 |
76 /* 77 * Have got an event to handle: 78 */ 79 fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs, 80 struct irqaction *action) 81 { 82 int ret, retval = 0, status = 0; 83 84 if (!(action->flags & SA_INTERRUPT)) 85 local_irq_enable(); 如果这个中断处理函数可以在开中断的环境下运行,那么将打开此CPU上的中断,因为在OS响应中断的时候,已经是把中断关闭了的。其实,打开这个中断也是无关痛痒的,因为对于同一类中断即使有表象上的中断嵌套,实际上他们之间还是简单的串行的,尽量避免开中断处理中断的建议只是对于中断间共享变量的情况的,可是实际上此类情况,即使关闭中断那么共享变量也是需要用spin_lock保护的。 86 87 do { 88 ret = action->handler(irq, action->dev_id, regs); 89 if (ret == IRQ_HANDLED) 90 status |= action->flags; 91 retval |= ret; 92 action = action->next; 93 } while (action); 94 95 if (status & SA_SAMPLE_RANDOM) 96 add_interrupt_randomness(irq); 97 local_irq_disable(); 98 99 return retval; 100 } |
175 spin_lock(&desc->lock); 176 if (!noirqdebug) 177 note_interrupt(irq, desc, action_ret, regs); 178 if (likely(!(desc->status & IRQ_PENDING))) 179 break; 如果在这个中断处理函数运行期间,又有新的中断抵达,那么这个CPU继续处理这个中断。(这个CPU真够累的,还好这种情况并不是很多) 180 desc->status &= ~IRQ_PENDING; 清除IRQ_PENDING位,因为只有一位,所以如果在中断处理期间又到达了多于一个的此类中断,那么也只有一个将被响应,如果中断不是很频繁,中断处理函数不是很耗时,就一切都还OK。另外,在极端情况下,有可能不停有中断抵达,那么这个OS就只能不停的响应中断,那么... ... 181 } 182 desc->status &= ~IRQ_INPROGRESS; 因为中断处理已经完成,所以将IRQ_INPROGRESS位清除 183 184 out: 185 /* 186 * The ->end() handler has to deal with interrupts which got 187 * disabled while the handler was running. 188 */ 189 desc->handler->end(irq); 收尾 190 spin_unlock(&desc->lock); 因为在168行的for循环中是带锁退出的,所以不要忘了释放锁 191 192 return 1; 193 } 194 |
162 /* 163 * Exit an interrupt context. Process softirqs if needed and possible: 164 */ 165 void irq_exit(void) 166 { 167 account_system_vtime(current); 168 sub_preempt_count(IRQ_EXIT_OFFSET); 将抢占计数器减去IRQ_EXIT_OFFSET,其实减去的数比开头加上的数少一,所以如果不是中断嵌套, 在下面的in_interrupt()判断中将返回否 169 if (!in_interrupt() && local_softirq_pending()) 170 invoke_softirq(); 如果有软中断需要响应,则响应之 171 preempt_enable_no_resched(); 完全恢复中断前的抢占计数器 172 } |