目前正在调试msix中断,在测试过程中发现会概率性的丢失中断。Msix中断默认是edge触发的中断,edge触发的中断是在中断对应pin发生电平信号跳变的时候,会发出一个中断请求。因为跳变是一瞬间的,不会像level触发中断那样一直保持电平不变,这样就可能会漏掉某一个跳变的瞬间,表现就是丢失了一个中断。
内核中处理edge触发中断的函数为handle_edge_irq,此函数有do_IRQ函数调用而来。
void handle_edge_irq(unsigned int irq, struct irq_desc *desc) { raw_spin_lock(&desc->lock); /*因为接收到中断,所以清除下面两个标志*/ desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); /*IRQ是否被禁止,或者正在处理中,或者没有挂接中断处理函数*/ if (unlikely(irqd_irq_disabled(&desc->irq_data) || irqd_irq_inprogress(&desc->irq_data) || !desc->action)) { if (!irq_check_poll(desc)) { desc->istate |= IRQS_PENDING; /*设置irq处于挂起状态*/ mask_ack_irq(desc); /*向硬件发ACK,屏蔽中断*/ goto out_unlock; } } kstat_incr_irqs_this_cpu(irq, desc); /*process/interrupts中对应irq计算加1*/ /*cpu对此IRQ的回应,表示准备接收此IRQ的下一个中断*/ desc->irq_data.chip->irq_ack(&desc->irq_data);
do { if (unlikely(!desc->action)) { /*没有挂接中断处理函数*/ mask_irq(desc); /*屏蔽IRQ*/ goto out_unlock; } /*IRQ处于中断挂起状态*/ if (unlikely(desc->istate & IRQS_PENDING)) { if (!irqd_irq_disabled(&desc->irq_data) && irqd_irq_masked(&desc->irq_data)) unmask_irq(desc); } /*处理中断*/ handle_irq_event(desc);
} while ((desc->istate & IRQS_PENDING) && /*有挂起的中断需要处理*/ !irqd_irq_disabled(&desc->irq_data)); /*irq没有禁止*/ |
istate成员刚开始在irq_desc结构体中没找到,其实它的定义如下
#define istate core_internal_state__do_not_mess_with_it
istate对应的是core_internal_state__do_not_mess_with_it成员。
IRQS_REPLAY标志是用来挽救丢失的中断。此标志在check_irq_resend函数中设置,通过中断控制器APIC上的中断信号重新向cpu发中断,而不是通过外设硬件来重发中断。这里进入到了handle_edge_irq函数表示已经收到了中断,不需要重发,所以清除此标志。
IRQS_WAITING标志表示中断的到来,这里收到了中断,因此也清除此标志。此标志在handle_xxx_irq函数的开始都会清除掉。
如果<1>当前irq被禁止,<2>当前irq正在处理中,<3>当前irq没有挂接处理函数,则我们不处理接收到的此IRQ。如果满足上面的条件,我们就设置此IRQ为IRQS_PENDING挂起状态,并屏蔽中断,然后返回。放弃中断的处理过程,留给正在处理此IRQ中断的cpu来处理此次pending的中断。
irq_ack函数的底层实现是写eoi寄存器,是cpu对此IRQ的回应,表示irq希望清除此IRQ的pending状态(用于清除APIC IRR的pending位),准备接收下一个中断。这样其他的cpu就可以接收此IRQ进行处理了。
程序接下来是一个while循环,循环的条件是此IRQ没有被禁止并且有pending的待处理的中断。因为在处理中断的过程中,因为ack了,可能其他的cpu又接收到了此IRQ新的中断。
如果此IRQ没有挂接中断处理函数,就直接屏蔽此IRQ,返回。
如果irq处于IRQS_PENDING状态,中断没有被禁止但是被屏蔽的情况下,调用unmask_irq取消屏蔽。想想为什么会处于IRQS_PENDING状态,并这样处理?前面讲到当此IRQ正在处理中,新接收到的中断,就会设置IRQS_PENDING状态,并屏蔽此IRQ。
Edge触发方式的中断容易丢中断,因此在处理中断时候不能长时间的屏蔽IRQ。在handle_edge_irq函数一上来就判断在irq没用被禁止,没用正在处理的情况下,只是ack回应了硬件,没用屏蔽irq,表示另外的cpu可以接收此IRQ的中断。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/weijitao/article/details/46953217