1. 程式人生 > >關於中斷處理程式中的關中斷函式disable_irq和disable_irq_nosync

關於中斷處理程式中的關中斷函式disable_irq和disable_irq_nosync

disable_irq關閉中斷並等待中斷處理完後返回, 而disable_irq_nosync立即返回. 那麼在中斷處理程式中應該使用哪一個函式來關閉中斷呢? 在<linux裝置驅動開發詳解>中的按鍵驅動中, 使用disable_irq來關閉中斷, 但是我在測試時進入中斷後系統會死在中斷處理程式, 而改為disable_irq_nosync則能正常退出中斷處理程式.下面從核心程式碼來找一下原因: 先看一下disable_irq_nosync,核心程式碼中是這樣解釋的:

/**
 *    disable_irq_nosync - disable an irq without waiting
 *    @irq: Interrupt to disable
 *
 *    Disable the selected interrupt line. Disables and Enables are
 *    nested.
 *    Unlike disable_irq(), this function does not ensure existing
 *    instances of the IRQ handler have completed before returning.


 *
 *    This function may be called from IRQ context.
 */
void disable_irq_nosync(unsigned int irq)
{
    struct irq_desc *desc = irq_to_desc(irq);
    unsigned long flags;

    if (!desc)
        return;

    chip_bus_lock(irq, desc);
    spin_lock_irqsave(&desc->lock, flags);
    __disable_irq(
desc, irq, false);
    spin_unlock_irqrestore(&desc->lock, flags);
    chip_bus_sync_unlock(irq, desc);
}


關閉中斷後程式返回, 如果在中斷處理程式中, 那麼會繼續將中斷處理程式執行完.

/**
 * disable_irq - disable an irq and wait for completion
 * @irq: Interrupt to disable
 *
 * Disable the selected interrupt line. Enables and Disables are
 * nested.
 * This function waits for any pending IRQ handlers for this interrupt
 * to complete before returning. If you use this function while
 * holding a resource the IRQ handler may need you will deadlock.
 *
 * This function may be called - with care - from IRQ context.
 */


void disable_irq(unsigned int irq)
{
        struct irq_desc *desc = irq_desc + irq;
        if (irq >= NR_IRQS)
                return;
        disable_irq_nosync(irq);
        if (desc->action)
                synchronize_irq(irq);
}


關閉中斷並等待中斷處理完後返回.從程式碼中可以看到, disable_irq先是呼叫了disable_irq_nosync, 然後檢測desc->action是否為1. 在中斷處理程式中, action是置1的, 所以進入synchronize_irq函式中.

/**
 * synchronize_irq - wait for pending IRQ handlers (on other CPUs)
 * @irq: interrupt number to wait for
 *
 * This function waits for any pending IRQ handlers for this interrupt
 * to complete before returning. If you use this function while
 * holding a resource the IRQ handler may need you will deadlock.
 *
 * This function may be called - with care - from IRQ context.
 */

void synchronize_irq(unsigned int irq)
{
 struct irq_desc *desc = irq_to_desc(irq);
 unsigned int status;
 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.
   */

  while (desc->status & IRQ_INPROGRESS)
   cpu_relax();
  /* Ok, that indicated we're done: double-check carefully. */
  spin_lock_irqsave(&desc->lock, flags);
  status = desc->status;
  spin_unlock_irqrestore(&desc->lock, flags);
  /* Oops, that failed? */
 } while (status & IRQ_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));
}


註釋中說明該函式是在等待中斷處理程式的結束, 這也是disable_irq與disable_irq_nosync不同的主要所在. 但是在中斷處理函式中呼叫會發生什麼情況呢? 進入中斷處理函式前IRQ_INPROGRESS會被__setup_irq設定, 所以程式會一直陷在while迴圈中, 而此時核心以經被獨佔, 這就導致系統死掉. 總結:
由於在disable_irq中會呼叫synchronize_irq函式等待中斷返回, 所以在中斷處理程式中不能使用disable_irq, 否則會導致cpu被synchronize_irq獨佔而發生系統崩潰. 最近在三星的s5pv210晶片上除錯intersil isl29028 light sensor驅動,驅動是在https://github.com/kernelzilla/android-kernel/blob/fe21dc28c3ccfdd0e1f059e3d896b1b989a7ea44/drivers/input/misc/isl29030.c找上,是motorola做的驅動,使用是在中斷處理函式中,使用到了disable_irq函式來遮蔽中斷,但每次對isl29028通過IIC匯流排設定其“配置暫存器”後,想再設定“中斷暫存器”時就會卡住,後面發現是中斷處理函式使用到disable_irq,而不是disable_irq_nosync函式造成的,但此時對於“配置暫存器”我設定的ALS和PROX功能都是對Enable位置為不使能的,但中斷還是發來了,可是驅動是在系統啟動過程載入的,此時很多中斷CPU都不可能處理的,就出現了卡死現象,但isl29028又沒有使能執行,不知道是不是light sensor晶片的問題,先這樣處理了。     對於disable_irq遮蔽中斷是會等待CPU處理,對CPU是獨佔的方式,而disable_irq_nosync則是馬上返回,不等待處理,故出現上面的情況。