linux中断里为什么不能使用disable_irq关中断?

在代码里想写一个这样的中断

static irqreturn_t xxx_interrupt(int irq, void *arg)
{
   disable_irq(irq);
   xxxxx
   enable_irq(irq);
}

使用的时候,发现系统会卡死在中断处理函数中.

追踪代码发现和使用disable_irq导致(linux3.2)

void disable_irq(unsigned int irq)
{
    if (!__disable_irq_nosync(irq))
	synchronize_irq(irq);
}

static int __disable_irq_nosync(unsigned int irq)
{
    unsigned long flags;
    struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);

    if (!desc)
        return -EINVAL;
    __disable_irq(desc, irq, false);
    irq_put_desc_busunlock(desc, flags);
    return 0;
}
从disable_irq的源码中我们可以看出
__disable_irq_nosync 
-> __disable_irq(desc, irq, false);
这里已经关闭了中断,而后面继续执行synchronize_irq函数

void synchronize_irq(unsigned int irq)
{
    struct irq_desc *desc = irq_to_desc(irq);
    bool inprogress;

    if (!desc)
        return;

    do {
        unsigned long flags;

        /*
         * Wait until we're out of the critical section.  This might
         * give the wrong answer due to the lack of memory barriers.
         */  
        /* 根据 IRQD_IRQ_INPROGRESS 标志位判断当前中断是不是还在处理 
         * 如果还在处理,则等待处理结束
         */
        while (irqd_irq_inprogress(&desc->irq_data))
            cpu_relax();

        /* Ok, that indicated we're done: double-check carefully. */  
        raw_spin_lock_irqsave(&desc->lock, flags);
        inprogress = irqd_irq_inprogress(&desc->irq_data);
        raw_spin_unlock_irqrestore(&desc->lock, flags);

        /* Oops, that failed? */
    } while (inprogress);

    /*
     * We made sure that no hardirq handler is running. Now verify
     * that no threaded handlers are active.
     */
    wait_event(desc->wait_for_threads, !atomic_read(&desc->threads_active));
}

static inline bool irqd_irq_inprogress(struct irq_data *d)
{
    return d->state_use_accessors & IRQD_IRQ_INPROGRESS;
}

也就是说disable_irq

一方面关闭中断 ( __disable_irq_nosync )

另一方面等待当前的中断结束( synchronize_irq )

这是好事,很严禁,但是放在中断函数中就不好了,代码会卡死在

while (irqd_irq_inprogress(&desc->irq_data))

卡死的原因说起来还是和中断的执行流程有关系,摘取部分中断执行流程如下:

handle_irq_event
    -> irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
        -> handle_irq_event_percpu
          -> xxx_interrupt
    -> irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
...

这样一贴流程很明显就看到原因了 IRQD_IRQ_INPROGRESS标志位的清除是在退出中断函数进行的,

而代码一直在中断函数中等待IRQD_IRQ_INPROGRESS标志位清空,不卡死才怪.

到了这里,问题原因就找到了!

那么,如果中断函数里关中断用什么api?

__disable_irq_nosync就可以,但是这个函数前面有下划线,看起来不是个api,查找一下调用

void disable_irq_nosync(unsigned int irq)
{
    __disable_irq_nosync(irq);
}

ok,以后就用disable_irq_nosync了.

static irqreturn_t xxx_interrupt(int irq, void *arg)
{
   disable_irq_nosync(irq);
   xxxxx
   enable_irq(irq);
}






你可能感兴趣的:(linux)