浅析armlinux 2.4.19中断irq分发例程的派发流程之高端层
文章来源:http://gliethttp.cublog.cn
接续前一篇《 浅析armlinux 2.4.19中断irq分发例程的派发流程之中间层》 看看高层asm_do_IRQ()和do_IRQ()中断处理函数[gliethttp_20071226].
//---------------------------------------------------------------------- //1.arch\arm\kernel\irq.c->asm_do_IRQ() asmlinkage void asm_do_IRQ(int irq, struct pt_regs *regs) {irq = fixup_irq(irq); if (irq < NR_IRQS) { int cpu = smp_processor_id(); //irq_enter(cpu, irq)对应的反汇编 //irq_stat[0].__local_irq_count++ //c00180d4: e59f4070 ldr r4, [pc, #70] ; c001814c //c00180d8: e5943004 ldr r3, [r4, #4] //c00180dc: e2833001 add r3, r3, #1 ; 0x1 //c00180e0: e5843004 str r3, [r4, #4] irq_enter(cpu, irq); spin_lock(&irq_controller_lock);//spin_lock函数无效 do_IRQ(irq, regs);//调用do_IRQ()处理该irq中断 if (!list_empty(&irq_pending)) do_pending_irqs(regs); spin_unlock(&irq_controller_lock); //irq_exit(cpu, irq)对应的反汇编 //irq_stat[0].__local_irq_count-- //r4=irq_stat //r2=irq_stat[0].__softirq_pending //r3=irq_stat[0].__local_irq_count //c0018100: e894000c ldmia r4, {r2, r3}
//c0018104: e3520000 cmp r2, #0 ; 0x0//对应if (softirq_pending(cpu)) //c0018108: e2433001 sub r3, r3, #1 ;//对应irq_stat[0].__local_irq_count-- //c001810c: e5843004 str r3, [r4, #4] irq_exit(cpu, irq); if (softirq_pending(cpu)) do_softirq();//如果有pending在链表上的softirq软中断,那么调用之 //include\asm-arm\arch-at91rm9200\irq.h->irq_finish() //#define irq_finish(irq) do { AT91_SYS->AIC_EOICR = 0; } while (0) irq_finish(irq);//标示at91rm9200硬件cpu中断处理完毕. return; } irq_err_count += 1;//错误统计加1 printk(KERN_ERR "IRQ: spurious interrupt %d\n", irq); irq_finish(irq); return; } //---------------------------------------------------------------------- //2.arch\arm\kernel\irq.c->do_IRQ() void do_IRQ(int irq, struct pt_regs * regs) {struct irqdesc *desc = irq_desc + irq;//该irq号对应的irqdesc描述结构体 desc->triggered = 1; desc->mask_ack(irq); //desc->running=1说明该irq的处理函数正在处理,说明该irq发生了嵌套中断 //对于快速中断处理函数,不会出现这种情况 if (desc->running || desc->disable_depth) goto running; //标示当前irq正在进行处理中 desc->running = 1; kstat.irqs[smp_processor_id()][irq]++;//当前irq中断统计次数加1 do { struct irqaction *action; action = desc->action;//当前irq处理函数结构体 if (!action) break; if (desc->pending && desc->disable_depth == 0) { //执行到这里irq是禁用的,所以pending可以安全置0 desc->pending = 0; desc->unmask(irq); } __do_irq(irq, action, regs);//执行有实际动作的irq中断处理函数体 } while (desc->pending && desc->disable_depth == 0); desc->running = 0;//完成 if (desc->action && !check_irq_lock(desc, irq, regs)) desc->unmask(irq); return; running: //该irq中断发生了中断嵌套重入 desc->pending = 1; } //---------------------------------------------------------------------- //3.arch\arm\kernel\irq.c->__do_irq() static void __do_irq(unsigned int irq, struct irqaction *action, struct pt_regs *regs) {unsigned int status; spin_unlock(&irq_controller_lock); //gliethttp_20071226 //SA_INTERRUPT置1表示为快速中断,也就是本地中断在执行快中断的 //时候,irq的中断将禁用,否则如果是慢中断那么irq的中断将开启 if (!(action->flags & SA_INTERRUPT)) //如果当前irq是慢中断,执行下面的语句开启arm的irq,这时会引起irq硬件中断嵌套 local_irq_enable(); status = 0; do { status |= action->flags; //如果慢中断,那么在执行action->handler慢中断处理函数的时候,因为irq已经打开,另一个irq //或者自己的irq嵌套可能又到来了,主要是因为action->handler处理函数体代码非常复杂, //总之是非常耗cpu的中断处理函数,当处理完之后,spin_lock_irq()再次禁用 //irq中断,剩下的do_IRQ收尾代码并不耗时,再次强调仅仅是慢中断的 //action->handler处理函数体非常耗用cpu而已[gliethttp_20071226]. action->handler(irq, action->dev_id, regs); //同一个irq中断源可能被多个处理程序共享,所以使用next将所有共享的 //irq处理函数遍历调用一次. action = action->next; } while (action);
if (status & SA_SAMPLE_RANDOM) //gliethttp_20071226 //该irq中断可以为内核的随机熵池做贡献 add_interrupt_randomness(irq); //gliethttp_20071226 //不论前面是否把irq中断使能了,这里都将调用spin_lock_irq再次禁用irq spin_lock_irq(&irq_controller_lock); } //---------------------------------------------------------------------- //4.arch\arm\kernel\irq.c->do_pending_irqs() static void do_pending_irqs(struct pt_regs *regs) {struct list_head head, *l, *n; do { struct irqdesc *desc;
head = irq_pending;//中断pending链表 INIT_LIST_HEAD(&irq_pending);//重新置空中断pending链表 head.next->prev = &head; head.prev->next = &head;
//遍历pending在链表上的所有被临时悬停阻塞的未处理中断 list_for_each_safe(l, n, &head) { desc = list_entry(l, struct irqdesc, pend); list_del_init(&desc->pend); do_IRQ(desc - irq_desc, regs);//处理pending在链表上的中断 } BUG_ON(!list_empty(&head)); } while (!list_empty(&irq_pending)); } //---------------------------------------------------------------------- //5.kernel\softirq.c->do_softirq() asmlinkage void do_softirq() { int cpu = smp_processor_id();//对于at91rm9200该cpu=0; __u32 pending; unsigned long flags; __u32 mask; /* include\asm-arm\hardirq.h->in_interrupt() #define in_interrupt() ({ const int __cpu = smp_processor_id(); \ (local_irq_count(__cpu) + local_bh_count(__cpu) != 0); }) */ if (in_interrupt())//所有irq中断处理函数都已经处理完毕,以及底半处理都已经完毕 return; /* #define local_irq_save(x) \ ({ \ unsigned long temp; \ __asm__ __volatile__( \ "mrs %0, cpsr @ local_irq_save\n" \ " orr %1, %0, #128\n" \ " msr cpsr_c, %1" \ : "=r" (x), "=r" (temp) \ : \ : "memory"); \ }) */ local_irq_save(flags);//将cpsr标志保存到flags中,同时禁用irq中断 //等同于pending = irq_stat[0].__softirq_pending; pending = softirq_pending(cpu); if (pending) { //pending非0,说明有需要处理的softirq事件 struct softirq_action *h; mask = ~pending; //local_bh_disable()等于irq_stat[0].__local_bh_count++ local_bh_disable(); restart: //在现在正关闭irq的时候清0__softirq_pending //softirq_pending()等于irq_stat[0].__softirq_pending = 0; softirq_pending(cpu) = 0; /* #define local_irq_enable() \ ({ \ unsigned long temp; \ __asm__ __volatile__( \ "mrs %0, cpsr @ local_irq_enable\n" \ " bic %0, %0, #128\n" \ " msr cpsr_c, %0" \ : "=r" (temp) \ : \ : "memory"); \ }) */ //打开irq中断,打开的一刹那,很可能就立即执行被阻塞着的irq中断. local_irq_enable(); h = softirq_vec; do { //static struct softirq_action softirq_vec[32]; //在linux2.4.19中softirq最大支持32个 //所以pending的bit31~bit0分别对应着这32个软中断占用情况[gliethttp_20071226] if (pending & 1) h->action(h); h++; pending >>= 1; } while (pending); local_irq_disable();//禁用中断 //等同于pending = irq_stat[0].__softirq_pending; pending = softirq_pending(cpu); if (pending & mask) { //mask中bit比特为1的数据位表示未登记softirq的项, //如果pending & mask非0,说明又发生了softirq登记 mask &= ~pending;//抹去新检测登记到的softirq项 goto restart;//跳过去继续执行嵌套了的另一个新中断 } //__local_bh_enable()等于irq_stat[0].__local_bh_count-- __local_bh_enable(); if (pending) //如果同一个softirq连续登记了2次以上,那么调用wakeup_softirqd()唤醒内核线程ksoftirqd //通过以上比较简单巧妙的方法可以轻松判断2次发生的同一个softirq wakeup_softirqd(cpu); } local_irq_restore(flags);//恢复之前的cpsr }
|