关于local_irq_disable和disable_irq的差异
发布时间:2021-11-12 11:52:44 所属栏目:教程 来源:互联网
导读:local_irq_disable: local_irq_disable的功能是屏蔽当前CPU上的所有中断,通过操作arm核心中的寄存器来屏蔽到达CPU上的中断,此时中断控制器中所有送往该CPU上的中断信号都将被忽略。 Kernel/arch/arm/include/asm/irqflag.h static inline void arch_local_
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后,中断控制器立刻就会感知到外设上的中断信号。由此进入中断处理流程。 ![]() (编辑:PHP编程网 - 黄冈站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |