免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 4532 | 回复: 10
打印 上一主题 下一主题

再提一个关于中断处理部分的问题,主要想请教各位__do_IRQ中的问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-04-01 17:52 |只看该作者 |倒序浏览
/*
* do_IRQ handles all normal device IRQ's (the special
* SMP cross-CPU interrupts have their own specific
* handlers).
*/
fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)
{
        irq_desc_t *desc = irq_desc + irq;
        struct irqaction * action;
        unsigned int status;

        kstat_this_cpu.irqs[irq]++;
        if (desc->status & IRQ_PER_CPU) {
                irqreturn_t action_ret;

                /*
                 * No locking required for CPU-local interrupts:
                 */
                desc->handler->ack(irq);
                action_ret = handle_IRQ_event(irq, regs, desc->action);
                if (!noirqdebug)
                        note_interrupt(irq, desc, action_ret);
                desc->handler->end(irq);
                return 1;
        }

        spin_lock(&desc->lock);//获得锁
        desc->handler->ack(irq);//做中断应答,同时锁irq对应的中断线,此时不应再产生同样类型的中断
        /*
         * REPLAY is when Linux resends an IRQ that was dropped earlier
         * WAITING is used by probe to mark irqs that are being tested
         */
        status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);//开始中断处理前初始化一些状态
        status |= IRQ_PENDING; /* we _want_ to handle it */

        /*
         * If the IRQ is disabled for whatever reason, we cannot
         * use the action we have.
         */
        action = NULL;
        if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {//判断是否在其他CPU上有相同类型的中断处理程
                                                                         //正在执行若有则直接退出
                action = desc->action;
                status &= ~IRQ_PENDING; /* we commit to handling */
                status |= IRQ_INPROGRESS; /* we are handling it */
        }
        desc->status = status;

        /*
         * If there is no IRQ handler or it was disabled, exit early.
         * Since we set PENDING, if another processor is handling
         * a different instance of this same irq, the other processor
         * will take care of it.
         */
        if (unlikely(!action))
                goto out;

        /*
         * Edge triggered interrupts need to remember
         * pending events.
         * This applies to any hw interrupts that allow a second
         * instance of the same irq to arrive while we are in do_IRQ
         * or in the handler. But the code here only handles the _second_
         * instance of the irq, not the third or fourth. So it is mostly
         * useful for irq hardware that does not mask cleanly in an
         * SMP environment.
         */
        for (; {
                irqreturn_t action_ret;

                spin_unlock(&desc->lock);

                action_ret = handle_IRQ_event(irq, regs, action);//执行真正的中断处理程序

                spin_lock(&desc->lock);
                if (!noirqdebug)
                        note_interrupt(irq, desc, action_ret);
                if (likely(!(desc->status & IRQ_PENDING)))//若没有同类型的中断处理程序在执行ISR时被触发则break;
                                                                  //否则继续循环
                                                                  //问题就在这里,既然前面已经关了对应irq的中断线,此处为何
                                                                  //还可能出现同类型的irq被触发,等待处理的情况呢?实际解除
                                                                  //中断线屏蔽状态的代码在后边注释处

                        break;
                desc->status &= ~IRQ_PENDING;
        }
        desc->status &= ~IRQ_INPROGRESS;

out:
        /*
         * The ->end() handler has to deal with interrupts which got
         * disabled while the handler was running.
         */
        desc->handler->end(irq);//打开被屏蔽的中断线
        spin_unlock(&desc->lock);

        return 1;
}

论坛徽章:
0
2 [报告]
发表于 2008-04-01 18:23 |只看该作者
原帖由 dengcainiao 于 2008-4-1 17:52 发表
/*
* do_IRQ handles all normal device IRQ's (the special
* SMP cross-CPU interrupts have their own specific
* handlers).
*/
fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_reg ...

我想原因是这个样子的:
在老版本的linux中,__do_IRQ的路径是被do_IRQ调用的,在执行do_IRQ时该中断线还没被mask,由于当前CPU的中断是关闭的,不会产生重复的中断。但在SMP情况,其它CPU的中断不是关闭的,当IOAPIC轮询到没有被mask的这条线时,可能再产生一个中断并route到其它CPU,这个时候另一个CPU就会产生一个PENDING。
所以这种情况应该出现在:
1、CPU1还在do_IRQ中执行,没有mask相应中断线
2、IOAPIC轮询到了该线,又产生了一个中断并route到其它CPU。

论坛徽章:
0
3 [报告]
发表于 2008-04-01 18:49 |只看该作者
原帖由 zx_wing 于 2008-4-1 18:23 发表

我想原因是这个样子的:
在老版本的linux中,__do_IRQ的路径是被do_IRQ调用的,在执行do_IRQ时该中断线还没被mask,由于当前CPU的中断是关闭的,不会产生重复的中断。但在SMP情况,其它CPU的中断不是关闭的, ...

分析的很有道理 。。。。,赞一个!

论坛徽章:
0
4 [报告]
发表于 2008-04-01 21:08 |只看该作者

继续提问。。

感觉在进行中断应答即desc->handler->ack(irq)时,执行了关对应中断线的操作。以8259A为例,实际执行的应该是mask_and_ack_8259A( )函数,我将该函数贴在下边:
static void mask_and_ack_8259A(unsigned int irq)
{
        unsigned int irqmask = 1 << irq;
        unsigned long flags;

        spin_lock_irqsave(&i8259A_lock, flags);
        /*
         * Lightweight spurious IRQ detection. We do not want
         * to overdo spurious IRQ handling - it's usually a sign
         * of hardware problems, so we only do the checks we can
         * do without slowing down good hardware unnecesserily.
         *
         * Note that IRQ7 and IRQ15 (the two spurious IRQs
         * usually resulting from the 8259A-1|2 PICs) occur
         * even if the IRQ is masked in the 8259A. Thus we
         * can check spurious 8259A IRQs without doing the
         * quite slow i8259A_irq_real() call for every IRQ.
         * This does not cover 100% of spurious interrupts,
         * but should be enough to warn the user that there
         * is something bad going on ...
         */
        if (cached_irq_mask & irqmask)
                goto spurious_8259A_irq;
        cached_irq_mask |= irqmask;

handle_real_irq:
        if (irq & {
                inb(PIC_SLAVE_IMR);        /* DUMMY - (do we need this?) */
                outb(cached_slave_mask, PIC_SLAVE_IMR);//如果我理解没错,该语句执行完后对应的中断线被屏蔽
                outb(0x60+(irq&7),PIC_SLAVE_CMD);/* 'Specific EOI' to slave */
                outb(0x60+PIC_CASCADE_IR,PIC_MASTER_CMD); /* 'Specific EOI' to master-IRQ2 */
        } else {
                inb(PIC_MASTER_IMR);        /* DUMMY - (do we need this?) */
                outb(cached_master_mask, PIC_MASTER_IMR);
                outb(0x60+irq,PIC_MASTER_CMD);        /* 'Specific EOI to master */
        }
        spin_unlock_irqrestore(&i8259A_lock, flags);
        return;
                。。。。。。。。
           。。。。。。。。

}
我的问题是,为什么在禁止本地中断后没在代码里发现设置IRQ_DISABLED的地方?在disable_irq()函数中关中断线的时候是设置了该位的,为什么这里没有?

论坛徽章:
0
5 [报告]
发表于 2008-04-01 21:27 |只看该作者
又考虑了一下,感觉在mask_and_ack_8259A( ) 中设不设IRQ_DISABLED实际上没什么用.
   另外ZT_WING除了你说的那种情况外,还有两种可能:
        1.当一个CPU在执行do_IRQ时,另一个CPU上假如显式调用了disable_irq()会导致那种情况.
            2.在执行__do_IRQ时,即使做了中断应答锁了中断线,当执行到如下代码时:
         for (; {
                irqreturn_t action_ret;

                spin_unlock(&desc->lock);

                action_ret = handle_IRQ_event(irq, regs, action);//这里已经释放了锁,另一个CPU如果显式调用enable_irq也会产生那种情况

                spin_lock(&desc->lock);
                if (!noirqdebug)
                        note_interrupt(irq, desc, action_ret, regs);
                if (likely(!(desc->status & IRQ_PENDING)))
                        break;
                desc->status &= ~IRQ_PENDING;
        }

不知道我考虑的是否正确,各位大牛指点

[ 本帖最后由 dengcainiao 于 2008-4-1 21:35 编辑 ]

论坛徽章:
0
6 [报告]
发表于 2008-04-01 21:35 |只看该作者
原帖由 dengcainiao 于 2008-4-1 21:27 发表
又考虑了一下,感觉在mask_and_ack_8259A( ) 中设不设IRQ_DISABLED实际上没什么用.
   另外ZT_WING除了你说的那种情况外,当一个CPU在执行do_IRQ时,另一个CPU上假如显式调用了disable_irq()也可能导致那种情况

mask中断线的时候本来就不应该设置IRQ_DISABLE,这样会阻止中断处理函数被调用。
你说的那种情况在另一个帖子里面已经说过了,就是ULK提到的lost interrupt

论坛徽章:
0
7 [报告]
发表于 2008-04-01 21:36 |只看该作者
哈哈,你回的真快,我编辑个帖子的功夫。。。
但是不知道你说的会阻止中断处理函数被调用是什么意思???

[ 本帖最后由 dengcainiao 于 2008-4-1 21:37 编辑 ]

论坛徽章:
0
8 [报告]
发表于 2008-04-01 21:55 |只看该作者
  spin_lock(&desc->lock);//获得锁
        desc->handler->ack(irq);//做中断应答,同时锁irq对应的中断线,此时不应再产生同样类型的中断
        /*
         * REPLAY is when Linux resends an IRQ that was dropped earlier
         * WAITING is used by probe to mark irqs that are being tested
         */
        status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);//开始中断处理前初始化一些状态
        status |= IRQ_PENDING; /* we _want_ to handle it */

        /*
         * If the IRQ is disabled for whatever reason, we cannot
         * use the action we have.
         */
        action = NULL;
        if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {//判断是否在其他CPU上有相同类型的中断处理程序
                                                                         //正在执行若有则直接退出
                action = desc->action;
                status &= ~IRQ_PENDING; /* we commit to handling */
                status |= IRQ_INPROGRESS; /* we are handling it */
        }
        desc->status = status;

就用贴的这段代码,你在ack的时候如果设了IRQ_DISABLED,那么后来根本就执行不到中断处理的handler。
自己关自己,呵呵

论坛徽章:
0
9 [报告]
发表于 2008-04-01 21:59 |只看该作者
:em11: 哎。。,可不是~~~!

论坛徽章:
0
10 [报告]
发表于 2008-04-17 15:19 |只看该作者
这哥俩说的不错,呵呵。

补充一点。

在handle_IRQ_event()中,有时候是开中断执行的:
if (!(action->flags & SA_INTERRUPT))
                local_irq_enable();

所以,这个时候也可能会再产生中断。

__do_IRQ执行结束的时候,应该所有的PENDING都处理了。
如果这个时候还有PENDING,就是lost interrupt, desc->handler->end(irq) 中要对其进行处理。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP