Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15317990
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: LINUX

2007-12-26 08:32:26

浅析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
}

阅读(2077) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~