免费注册 查看新帖 |

Chinaunix

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

ULK第3版第4.6章中关于IRQ_REPLAY的问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-06-10 14:18 |只看该作者 |倒序浏览
enable_irq时,如果发现还有IRQ_PENDING,就说明存在丢失的中断,需要挽救。
但关于IRQ_REPLAY,书中说是为了防止多次产生一个丢失的中断。
我的问题是:
即使不使用IRQ_REPLAY,在什么情况下会多次产生一个丢失的中断呢?
代码中,spin_lock已经保护了整段代码,并且hw_resend_irq是LAPIC发出的,会立即在本处理器上产生,也就是说,
执行到最后的spin_unlock(这里书中写错了,不是spin_lock)时,根本没有机会再次进入这段代码,那么怎么会产生“多次产生一个丢失的中断”的情况呢?
ULK 原文如下:
    spin_lock_irqsave(&(irq_desc[irq].lock), flags);
    if (--irq_desc[irq].depth == 0) {
        irq_desc[irq].status &= ~IRQ_DISABLED;
        if (irq_desc[irq].status & (IRQ_PENDING | IRQ_REPLAY))
               == IRQ_PENDING) {
            irq_desc[irq].status |= IRQ_REPLAY;
            hw_resend_irq(irq_desc[irq].handler,irq);
        }
        irq_desc[irq].handler->enable(irq);
    }
    spin_lock_irqrestore(&(irq_desc[irq].lock), flags);
The function detects that an interrupt was lost by checking the value of the IRQ_PENDING flag. The flag is always cleared when leaving the interrupt handler; therefore, if the IRQ line is disabled and the flag is set, then an interrupt occurrence has been acknowledged but not yet serviced. In this case the hw_resend_irq( ) function raises a new interrupt. This is obtained by forcing the local APIC to generate a self-interrupt (see the later section "Interprocessor Interrupt Handling").
***The role of the IRQ_REPLAY flag is to ensure that exactly one self-interrupt is generated. ***
Remember that the _ _do_IRQ( ) function clears that flag when it starts handling the interrupt.

论坛徽章:
0
2 [报告]
发表于 2008-06-10 16:32 |只看该作者
__do_IRQ在ack的时候清掉REPLAY,但是如果由于关中断了,do_IRQ在多个renable之间没有得到运行,即ack在replay之后还没有运行,那么没有必要再replay了

论坛徽章:
0
3 [报告]
发表于 2008-06-10 16:38 |只看该作者
假如在执行完enable后还未执行中断程序时,重新有一个disable之后enable的过程,是否会导致重复hw_resend_irq?我感觉是有可能的。IRQ_REPLY应该是为了避免这种情况设计的。

[ 本帖最后由 dengcainiao 于 2008-6-10 16:40 编辑 ]

论坛徽章:
0
4 [报告]
发表于 2008-06-10 18:31 |只看该作者
原帖由 dengcainiao 于 2008-6-10 16:38 发表
假如在执行完enable后还未执行中断程序时,重新有一个disable之后enable的过程,是否会导致重复hw_resend_irq?我感觉是有可能的。IRQ_REPLY应该是为了避免这种情况设计的。

是这样的哈。
刚才我没注意到这是在关中断情况下执行的,这个时候IPI要等到开中断时才会被LAPIC提交到CPU。所以其它CPU有机会在spin_lock_irqrestore(&(irq_desc[irq].lock), flags);之后再执行一次disable/enable的过程,导致重复。

论坛徽章:
0
5 [报告]
发表于 2008-06-11 09:01 |只看该作者
原帖由 dengcainiao 于 2008-6-10 16:38 发表
假如在执行完enable后还未执行中断程序时,重新有一个disable之后enable的过程,是否会导致重复hw_resend_irq?我感觉是有可能的。IRQ_REPLY应该是为了避免这种情况设计的。

谢谢!
因为发生中断感觉是在enable中完成的(执行spin_unlock的瞬间),我是否能把你的话理解成:假如在这次执行到enable函数内部,但还未执行中断程序时,重新有一个disable之后enable的过程...?

但我认为这时不存在重新的disable再enable。
因为这次的enable开始时已经spin_lock了,再一次的enable无法执行下去。
所以还是有些疑问。

[ 本帖最后由 OstrichFly 于 2008-6-11 09:32 编辑 ]

论坛徽章:
0
6 [报告]
发表于 2008-06-11 09:06 |只看该作者
中断不是在enable中完成的,如ZX_WING兄弟说的IPI在开中断以后才会被提交,即在spin_lock_irqrestore(&(irq_desc[irq].lock), flags);之后。所以说中断完成应该是在enable函数外边,在二者之间有机会插入一组diable/enable的过程。

[ 本帖最后由 dengcainiao 于 2008-6-11 09:07 编辑 ]

论坛徽章:
0
7 [报告]
发表于 2008-06-11 09:23 |只看该作者
原帖由 zx_wing 于 2008-6-10 18:31 发表

是这样的哈。
刚才我没注意到这是在关中断情况下执行的,这个时候IPI要等到开中断时才会被LAPIC提交到CPU。所以其它CPU有机会在spin_lock_irqrestore(&(irq_desc.lock), flags);之后再执行一次disable/enabl ...

谢谢!
这样看来,中断应该发生在spin_unlock_irqrestore的内部,也就是执行开中断指令的瞬间,会立即进入中断服务程序。
如果说会再一次发生disable/enable,只有在这一瞬间的空档中发生,或者由于时间中断引起抢占调度,请问是这样的吗?

论坛徽章:
0
8 [报告]
发表于 2008-06-11 09:32 |只看该作者
原帖由 flw2 于 2008-6-10 16:32 发表
__do_IRQ在ack的时候清掉REPLAY,但是如果由于关中断了,do_IRQ在多个renable之间没有得到运行,即ack在replay之后还没有运行,那么没有必要再replay了

谢谢!
你的意思是:如果关中断的时候进行了renable,是不应该再次发生中断的,replay是为了这个目的设置的?
但即使没有replay,“关中断”本身也能阻止再次发生中断阿,那么replay的必要性在什么地方呢?

论坛徽章:
0
9 [报告]
发表于 2008-06-11 09:38 |只看该作者
原帖由 OstrichFly 于 2008-6-11 09:32 发表

谢谢!
你的意思是:如果关中断的时候进行了renable,是不应该再次发生中断的,replay是为了这个目的设置的?
但即使没有replay,“关中断”本身也能阻止再次发生中断阿,那么replay的必要性在什么地方呢?

不是关中断的时候进行enable,是enable的时候进行了关中断,加锁的同时关了中断啊
spin_lock_irqsave(&desc->lock, flags);//这里加锁并关中断,同时保存中断当前状态
        switch (desc->depth) {
        case 0:
                WARN_ON(1);
                break;
        case 1: {
                unsigned int status = desc->status & ~IRQ_DISABLED;

                desc->status = status;
                if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) {
                        desc->status = status | IRQ_REPLAY;
                        hw_resend_irq(desc->handler,irq);
                }
                desc->handler->enable(irq);
                /* fall-through */
        }
        default:
                desc->depth--;
        }
        spin_unlock_irqrestore(&desc->lock, flags);//解锁,同时恢复加锁前的中断状态

[ 本帖最后由 dengcainiao 于 2008-6-11 10:33 编辑 ]

论坛徽章:
0
10 [报告]
发表于 2008-06-11 10:09 |只看该作者
原帖由 dengcainiao 于 2008-6-11 09:06 发表
中断不是在enable中完成的,如ZX_WING兄弟说的IPI在开中断以后才会被提交,即在spin_lock_irqrestore(&(irq_desc.lock), flags);之后。所以说中断完成应该是在enable函数外边,在二者之间有机会插入一组diable/ ...

谢谢!
我理解了你说的“IPI在开中断以后才会被提交,即在spin_lock_irqrestore(&(irq_desc.lock), flags);之后”。
但严格来说,提交IPI应该是在spin_lock_irqrestore(&(irq_desc.lock), flags)内部,中断即被打开,然后控制路径会立即进入中断处理代码,执行完中断处理代码后,才会从spin_unlock_irqrestore函数中退出,然后继续执行enable函数的结尾部分。

谢谢你们的回答,以下我现在的理解,不知道对不对:
如果要发生多次的中断,必须在上文中的这段执行路径中又一次发生disable/enable。
对于其他CPU,就需要在spin_unlock_irqrestore打开锁之后,以远远超越当前这个CPU的速度飞快地完成disable->enable->中断执行的过程,而又不能快到执行了_ _do_IRQ( )中的if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))。
而这段时间里这个CPU必须恰好不紧不慢的执行"进入中断->_ _do_IRQ( )"的过程,但又不能执行到if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))。
(否则这2个中断又可能被合并了)
然后,其中一个CPU以飞快的速度迅速完成if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))之后的全部中断处理工作,而且必须赶在另一个CPU执行if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))之前。这才能避免2个中断被IRQ_INPROGRESS合并。

如果只考虑同一个CPU,感觉不太可能发生2次中断,唯一要考虑的就是spin_unlock_irqrestore执行中,但还没有进入中断服务之间这段时间可能发生的内核抢占。但如果抢占了,抢占后会首先执行中断处理,直到irq_desc[irq].handler->end(irq);,等到回到原先的任务时,已经没有中断了。

天哪,重看一便,感觉写的好乱,但也没办法改得容易读一些。

[ 本帖最后由 OstrichFly 于 2008-6-11 10:21 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP