早期的內核版本中,幾乎所有的中斷都是由__do_IRQ函數進行處理,但是,因爲各種中斷請求的電氣特性會有所不同,又或者中斷控制器的特性也不同,這會導致以下這些處理也會有所不同:
- 何時對中斷控制器發出ack迴應;
- mask_irq和unmask_irq的處理;
- 中斷控制器是否需要eoi迴應?
- 何時打開cpu的本地irq中斷?以便允許irq的嵌套;
- 中斷數據結構的同步和保護;
聲明:本博內容均由http://blog.csdn.net/droidphone原創,轉載請註明出處,謝謝!
爲此,通用中斷子系統把幾種常用的流控類型進行了抽象,併爲它們實現了相應的標準函數,我們只要選擇相應的函數,賦值給irq所對應的irq_desc結構的handle_irq字段中即可。這些標準的回調函數都是irq_flow_handler_t類型:
目前的通用中斷子系統實現了以下這些標準流控回調函數,這些函數都定義在:kernel/irq/chip.c中,
- handle_simple_irq 用於簡易流控處理;
- handle_level_irq 用於電平觸發中斷的流控處理;
- handle_edge_irq 用於邊沿觸發中斷的流控處理;
- handle_fasteoi_irq 用於需要響應eoi的中斷控制器;
- handle_percpu_irq 用於只在單一cpu響應的中斷;
- handle_nested_irq 用於處理使用線程的嵌套中斷;
驅動程序和板級代碼可以通過以下幾個API設置irq的流控函數:
- irq_set_handler();
- irq_set_chip_and_handler();
- irq_set_chip_and_handler_name();
以下這個序列圖展示了整個通用中斷子系統的中斷響應過程,flow_handle一欄就是中斷流控層的生命週期:
圖1.1 通用中斷子系統的中斷響應過程
2. handle_simple_irq
該函數沒有實現任何實質性的流控操作,在把irq_desc結構鎖住後,直接調用handle_irq_event處理irq_desc中的action鏈表,它通常用於多路複用(類似於中斷控制器級聯)中的子中斷,由父中斷的流控回調中調用。或者用於無需進行硬件控制的中斷中。以下是它的經過簡化的代碼:
- void
- handle_simple_irq(unsigned int irq, struct irq_desc *desc)
- {
- raw_spin_lock(&desc->lock);
- ......
- handle_irq_event(desc);
- out_unlock:
- raw_spin_unlock(&desc->lock);
- }
3. handle_level_irq
- void
- handle_level_irq(unsigned int irq, struct irq_desc *desc)
- {
- raw_spin_lock(&desc->lock);
- mask_ack_irq(desc);
- if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
- goto out_unlock;
- ......
- if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data)))
- goto out_unlock;
- handle_irq_event(desc);
- if (!irqd_irq_disabled(&desc->irq_data) && !(desc->istate & IRQS_ONESHOT))
- unmask_irq(desc);
- out_unlock:
- raw_spin_unlock(&desc->lock);
- }
狀態 | 紅色 | 綠色 |
---|---|---|
IRQ_PROGRESS | TRUE | FALSE |
是否允許本地cpu中斷 | 禁止 | 允許 |
是否允許該設備再次觸發中斷(可能由其它cpu響應) | 禁止 | 允許 |
4. handle_edge_irq
狀態 | 紅色 | 綠色 |
IRQ_PROGRESS | TRUE | FALSE |
是否允許本地cpu中斷 | 禁止 | 允許 |
是否允許該設備再次觸發中斷(可能由其它cpu響應) | 禁止 | 允許 |
是否處於中斷上下文 | 處於中斷上下文 | 處於進程上下文 |
由圖4.1也可以看出,在處理軟件中斷(softirq)期間,此時仍然處於中斷上下文中,但是cpu的本地中斷是處於打開狀態的,這表明此時嵌套中斷允許發生,不過這不要緊,因爲重要的處理已經完成,被嵌套的也只是軟件中斷部分而已。
5. handle_fasteoi_irq
- void
- handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
- {
- raw_spin_lock(&desc->lock);
- if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
- if (!irq_check_poll(desc))
- goto out;
- ......
- if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
- desc->istate |= IRQS_PENDING;
- mask_irq(desc);
- goto out;
- }
- if (desc->istate & IRQS_ONESHOT)
- mask_irq(desc);
- preflow_handler(desc);
- handle_irq_event(desc);
- out_eoi:
- desc->irq_data.chip->irq_eoi(&desc->irq_data);
- out_unlock:
- raw_spin_unlock(&desc->lock);
- return;
- ......
- }
6. handle_percpu_irq
- void
- handle_percpu_irq(unsigned int irq, struct irq_desc *desc)
- {
- struct irq_chip *chip = irq_desc_get_chip(desc);
- kstat_incr_irqs_this_cpu(irq, desc);
- if (chip->irq_ack)
- chip->irq_ack(&desc->irq_data);
- handle_irq_event_percpu(desc, desc->action);
- if (chip->irq_eoi)
- chip->irq_eoi(&desc->irq_data);
- }
7. handle_nested_irq
該函數用於實現其中一種中斷共享機制,當多箇中斷共享某一根中斷線時,我們可以把這個中斷線作爲父中斷,共享該中斷的各個設備作爲子中斷,在父中斷的中斷線程中決定和分發響應哪個設備的請求,在得出真正發出請求的子設備後,調用handle_nested_irq來響應中斷。所以,該函數是在進程上下文執行的,我們也無需掃描和執行irq_desc結構中的action鏈表。父中斷在初始化時必須通過irq_set_nested_thread函數明確告知中斷子系統:這些子中斷屬於線程嵌套中斷類型,這樣驅動程序在申請這些子中斷時,內核不會爲它們建立自己的中斷線程,所有的子中斷共享父中斷的中斷線程。
- void handle_nested_irq(unsigned int irq)
- {
- ......
- might_sleep();
- raw_spin_lock_irq(&desc->lock);
- ......
- action = desc->action;
- if (unlikely(!action || irqd_irq_disabled(&desc->irq_data)))
- goto out_unlock;
- irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
- raw_spin_unlock_irq(&desc->lock);
- action_ret = action->thread_fn(action->irq, action->dev_id);
- raw_spin_lock_irq(&desc->lock);
- irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
- out_unlock:
- raw_spin_unlock_irq(&desc->lock);
- }