local_irq_disable:
local_irq_disable的功能是遮蔽當前CPU上的所有中斷,通過操作arm核心中的暫存器來遮蔽到達CPU上的中斷,此時中斷控制器中所有送往該CPU上的中斷訊號都將被忽略。
Kernel/arch/arm/include/asm/irqflag.h
static inline void arch_local_irq_disable(void)
{
asm volatile(
" cpsid i @ arch_local_irq_disable"
:
:
: "memory", "cc");
}
kernel/include/linux/irqflags.h
#define raw_local_irq_disable() arch_local_irq_disable()
#define local_irq_disable() \
do { raw_local_irq_disable(); trace_hardirqs_off(); } while (0)
disable_irq:
在全域性範圍內遮蔽某一箇中斷號(irq num)。該irq num對應的irq handler不會在任何一個CPU上執行。這個操作是通過設定中斷控制器中的暫存器來對指定中斷進行遮蔽,而其他未遮蔽的中斷依然可以正常送往CPU。
413 void disable_irq(unsigned int irq)
414 {
415 if (!__disable_irq_nosync(irq))
416 synchronize_irq(irq);
417 }
372 static int __disable_irq_nosync(unsigned int irq)
373 {
374 unsigned long flags;
375 struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_ CHECK_GLOBAL);
376
377 if (!desc)
378 return -EINVAL;
379 __disable_irq(desc, irq, false);
380 irq_put_desc_busunlock(desc, flags);
381 return 0;
382 }
383
360 void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend)
361 {
362 if (suspend) {
363 if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND))
364 return;
365 desc->istate |= IRQS_SUSPENDED;
366 }
367
368 if (!desc->depth++)
369 irq_disable(desc);
370 }
chip.c
216 void irq_disable(struct irq_desc *desc)
217 {
218 irq_state_set_disabled(desc);
219 if (desc->irq_data.chip->irq_disable) {
220 desc->irq_data.chip->irq_disable(&desc->irq_data);
221 irq_state_set_masked(desc);
222 }
223 }
160 static void irq_state_set_disabled(struct irq_desc *desc)
161 {
162 irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED);
163 }
一箇中斷處理的流程是這樣的:
關CPU中斷——–>mask and ack interrupt controller——–>裝置驅動中註冊的irq_handler ——>unmask interrupt controller——–>開CPU中斷
我們需要在irq_handler中做如下處理,其中包含了一個啟動下半部softirq的操作(可選)。
ack device irq——–>copy data to ram——>raise softirq
在程式碼中,是這樣的呼叫流程:
High level irq handler
–> mask_ack_irq
–>chip->irq_mask
–>chip->irq_ack
–> handle_irq_event (就是呼叫irq_handler的處理)
–>chip-> irq_unmask
具體可以參考蝸窩上的文章,對兩種場景有比較詳細的介紹。我們接下來討論電平觸發的場景,來看看如何在所有CPU上進行遮蔽中斷的。其他場景可以舉一反三。
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)))
if (!irq_check_poll(desc))
goto out_unlock;
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);--和retrigger中斷以及自動探測IRQ相關
kstat_incr_irqs_this_cpu(irq, desc);
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
desc->istate |= IRQS_PENDING;
goto out_unlock;
}
handle_irq_event(desc);
cond_unmask_irq(desc);
out_unlock:
raw_spin_unlock(&desc->lock);
}
從程式碼中可以看到,在函式中首先做的就是mask_ack_irq,在其中會呼叫chip中的回撥來設定硬體。
static inline void mask_ack_irq(struct irq_desc *desc)
{
if (desc->irq_data.chip->irq_mask_ack)
desc->irq_data.chip->irq_mask_ack(&desc->irq_data);
else {
desc->irq_data.chip->irq_mask(&desc->irq_data);
if (desc->irq_data.chip->irq_ack)
desc->irq_data.chip->irq_ack(&desc->irq_data);
}
irq_state_set_masked(desc);
}
該函式中呼叫的就是chip中的irq_mask和irq_ack來操作chip中的暫存器.其中的irqd_irq_disabled就是用來判斷該中斷是否被其他CPU給disable了,這裡的disable就是呼叫disable_irq函式來做的,由此可見,使用disable_irq會在所有的CPU上把中斷號給遮蔽掉。
當在一個CPU上呼叫了disable_irq的時候,可能另一個CPU已經接收了中斷了,但是在handler的處理中可以看到,它會判斷是否被其它CPU disable了,如果disable了,它會把這個中斷標誌設定為IRQS_PENDING,但並不會去執行irq handler,而是直接退出,此時也沒有呼叫unmask函式,由此就遮蔽了該中斷,注意這裡的mask和ack只是對於中斷控制器到CPU上的訊號進行了遮蔽,而外設到中斷控制器上的中斷訊號並沒有消失。
而在使能中斷函式enable_irq中,我們可以看到它會呼叫unmask來取消該中斷的遮蔽。由於是電平觸發,所以當unmask後,中斷控制器立刻就會感知到外設上的中斷訊號。由此進入中斷處理流程。