Chinaunix首页 | 论坛 | 博客
  • 博客访问: 160634
  • 博文数量: 47
  • 博客积分: 1032
  • 博客等级: 少尉
  • 技术积分: 759
  • 用 户 组: 普通用户
  • 注册时间: 2011-01-19 15:47
文章分类
文章存档

2012年(26)

2011年(21)

分类: LINUX

2011-08-15 15:43:17

linux-2.6.20
/usr/src/linux/include/asm-x86_64/hw_irq.h +122
---------------------------------------------
#define BUILD_IRQ(nr) \
asmlinkage void IRQ_NAME(nr); \
__asm__( \
"\n.p2align\n" \
"IRQ" #nr "_interrupt:\n\t" \
    "push $~(" #nr ") ; " \
    "jmp common_interrupt");



/usr/src/linux/arch/i386/kernel/entry.S +206
---------------------------------------------
common_interrupt:
    SAVE_ALL
    TRACE_IRQS_OFF
    movl %esp,%eax
    call do_IRQ
    jmp ret_from_intr
    CFI_ENDPROC


                                   handle_fasteoi_irq()
                                   handle_edge_irq()
common_interrupt:
--> do_IRQ()
--> handle_irq() --> handle_IRQ_event() --> action->handler()

 

do_IRQ() -- 中断机制入口函数
 

common_interrupt --> do_IRQ()

linux-2.6.20
vi /usr/src/linux/arch/i386/kernel/irq.c +54
---------------------------------------------
astcall unsigned int do_IRQ(struct pt_regs *regs)
{
    struct pt_regs *old_regs;
    int irq = ~regs->orig_eax;
    struct irq_desc *desc = irq_desc + irq;
#ifdef CONFIG_4KSTACKS
    union irq_ctx *curctx, *irqctx;
    u32 *isp;
#endif
    if (unlikely((unsigned)irq >= NR_IRQS)) {
        printk(KERN_EMERG "%s: cannot handle IRQ %d\n",
                    __FUNCTION__, irq);
        BUG();
    }

    old_regs = set_irq_regs(regs);

增加中断处理程序的嵌套计数
|-------------------|
|   irq_enter();    |
|-------------------|

#ifdef CONFIG_DEBUG_STACKOVERFLOW
    {
        long esp;
        __asm__ __volatile__("andl %%esp,%0" :
                    "=r" (esp) : "0" (THREAD_SIZE - 1));
        if (unlikely(esp < (sizeof(struct thread_info) + STACK_WARN))) {
            printk("do_IRQ: stack overflow: %ld\n",
                esp - sizeof(struct thread_info));
            dump_stack();
        }
    }
#endif

#ifdef CONFIG_4KSTACKS
    curctx = (union irq_ctx *) current_thread_info();
    irqctx = hardirq_ctx[smp_processor_id()];
    if (curctx != irqctx) {
        int arg1, arg2, ebx;
        isp = (u32*) ((char*)irqctx + sizeof(*irqctx));
        irqctx->tinfo.task = curctx->tinfo.task;
        irqctx->tinfo.previous_esp = current_stack_pointer;
        irqctx->tinfo.preempt_count =
            (irqctx->tinfo.preempt_count & ~SOFTIRQ_MASK) |
            (curctx->tinfo.preempt_count & SOFTIRQ_MASK);
        asm volatile(
            "       xchgl %%ebx,%%esp      \n"
            "       call   *%%edi           \n"
            "       movl   %%ebx,%%esp      \n"
            : "=a" (arg1), "=d" (arg2), "=b" (ebx)
            : "0" (irq),   "1" (desc), "2" (isp),
               "D" (desc->handle_irq)
            : "memory", "cc"
        );
    } else
#endif

        desc->handle_irq(irq, desc);
    irq_exit();
    set_irq_regs(old_regs);
    return 1;
}


handle_edge_irq()
 
do_IRQ() --> handle_edge_irq()

void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
    const unsigned int cpu = smp_processor_id();
    spin_lock(&desc->lock);
    desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
    if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
            !desc->action)) {
        desc->status |= (IRQ_PENDING | IRQ_MASKED);
        mask_ack_irq(desc, irq);
        goto out_unlock;
    }
    kstat_cpu(cpu).irqs[irq]++;
    desc->chip->ack(irq);
    desc->status |= IRQ_INPROGRESS;
    do {
        struct irqaction *action = desc->action;
        irqreturn_t action_ret;
        if (unlikely(!action)) {
            desc->chip->mask(irq);
            goto out_unlock;
        }
        if (unlikely((desc->status &
                   (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
                  (IRQ_PENDING | IRQ_MASKED))) {
            desc->chip->unmask(irq);
            desc->status &= ~IRQ_MASKED;
        }   
        desc->status &= ~IRQ_PENDING;
        spin_unlock(&desc->lock);
        action_ret = handle_IRQ_event(irq, action);
        if (!noirqdebug)
            note_interrupt(irq, desc, action_ret);
        spin_lock(&desc->lock);

    } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
    desc->status &= ~IRQ_INPROGRESS;
out_unlock:
    spin_unlock(&desc->lock);
}



handle_IRQ_event()
 
handle_edge_irq() --> handle_IRQ_event()

irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
    irqreturn_t ret, retval = IRQ_NONE;
    unsigned int status = 0;
    handle_dynamic_tick(action);
    if (!(action->flags & IRQF_DISABLED))
        local_irq_enable_in_hardirq();
    do {
        ret = action->handler(irq, action->dev_id);
        if (ret == IRQ_HANDLED)
            status |= action->flags;
        retval |= ret;
        action = action->next;
    } while (action);
    if (status & IRQF_SAMPLE_RANDOM)
        add_interrupt_randomness(irq);
    local_irq_disable();
    return retval;
}


注:
(1) 内核接收到一个中断后,它将依次调用在该中断线上注册的每一个处理程序。因此,一个处理程序知道它是否应该为这个中断负责。如果与它相关的设备并没有产生中断,那么处理程序应该立即退出。这需要硬件设备提供状态寄存器,以便中断处理程序进行检查。毫无疑问,大多数硬件都提供这种功能。

(2) USB设备的中断处理
action->handlerusb_hcd_irq()
    USB主控制器提供了中断状态寄存器
regs->intrstatus来保存中断发生的具体情况,这个32位的中断状态寄存器便是用来供中断处理程序判断中断是否由USB设备触发




阅读(1608) | 评论(0) | 转发(1) |
0

上一篇:没有了

下一篇:struct irq_desc

给主人留下些什么吧!~~