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獨佔而發生系統崩潰.