概述
local_irq_disable() , local_irq_enable() , local_irq_save() 和 local_irq_restore() 为中断处理函数,主要是在要进入临界区时禁止中断和在出临界区时使能中断。
local_irq_disable() 和 local_irq_enable() 配对使用;
local_irq_save() 和 local_irq_restore() 配对使用。
local_irq_disable() 和 local_irq_save() 都可以禁止中断,但不同的是后者可以保存中断状态。
local_irq_restore() 在使能中断的同时还恢复了由 local_irq_save() 所保存的中断状态。
在一个关闭中断的环境中调用 local_irq_disable和 local_irq_enable后会破坏之前的中断响应状态。尽管调用 local_irq_disable前是关中断的环境,但是在调用 local_irq_enable后却变成开中断,这显然不是我们希望的 。
调用 local_irq_restore后不一定会开启中断,只会恢复调用 local_irq_save之前的中断状态,如果调用 local_irq_save之前是开中断,那么就打开中断; 如果调用 local_irq_save之前是关中断,那么就关闭中断。
所以 local_irq_save和 local_irq_restore会更安全。
1.local_irq_enable
#define local_irq_enable() \
do { \
trace_hardirqs_on(); \
raw_local_irq_enable(); \
} while (0)
#define raw_local_irq_enable() arch_local_irq_enable()
1.1 arch_local_irq_enable
static inline void arch_local_irq_enable(void)
{
if (system_has_prio_mask_debugging()) {
u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
}
asm volatile(ALTERNATIVE(
"msr daifclr, #2 // arch_local_irq_enable",
__msr_s(SYS_ICC_PMR_EL1, "%0"),
ARM64_HAS_IRQ_PRIO_MASKING)
:
: "r" ((unsigned long) GIC_PRIO_IRQON)
: "memory");
pmr_sync();
}
1.system_has_prio_mask_debugging()
:这个函数用于检查系统是否支持优先级屏蔽调试。如果系统支持,它会检查 GIC 中断控制器的优先级屏蔽状态. 在linux上不支持,所以不关心这里。
2.asm volatile(ALTERNATIVE(...))
:这部分是使用内联汇编实现的关键代码,根据条件编译指令 ARM64_HAS_IRQ_PRIO_MASKING
来选择不同的汇编指令。具体做法是根据是否支持 IRQ 优先级屏蔽来决定使用 msr daifclr, #2
还是 msr_s(SYS_ICC_PMR_EL1, "%0")
。
我们这里执行的是:msr daifclr, #2
3.pmr_sync:这个函数用于确保 GIC 中断控制器的操作同步完成。
#ifdef CONFIG_ARM64_PSEUDO_NMI
#define pmr_sync() \
do { \
extern struct static_key_false gic_pmr_sync; \
\
if (static_branch_unlikely(&gic_pmr_sync)) \
dsb(sy); \
} while(0)
#else
#define pmr_sync() do {} while (0)
#endif
2.local_irq_disable
#define local_irq_disable() \
do { \
bool was_disabled = raw_irqs_disabled();\
raw_local_irq_disable(); \
if (!was_disabled) \
trace_hardirqs_off(); \
} while (0)
local_irq_disable这个宏主要做了3个工作:
调用函数raw_irqs_disabled读取irq的状态;
调用函数raw_local_irq_disable关中断;
判断步骤一获取到的状态是否关闭,如果还没有关闭,就执行trace_hardirqs_off函数进行trace。
2.1 raw_irqs_disabled
#define raw_irqs_disabled() (arch_irqs_disabled())
static inline int arch_irqs_disabled(void)
{
return arch_irqs_disabled_flags(arch_local_save_flags());
}
static inline unsigned long arch_local_save_flags(void)
{
unsigned long flags;
asm volatile(ALTERNATIVE(
"mrs %0, daif",
__mrs_s("%0", SYS_ICC_PMR_EL1),
ARM64_HAS_IRQ_PRIO_MASKING)
: "=&r" (flags)
:
: "memory");
return flags;
}
static inline int arch_irqs_disabled_flags(unsigned long flags)
{
int res;
asm volatile(ALTERNATIVE(
"and %w0, %w1, #" __stringify(PSR_I_BIT),
"eor %w0, %w1, #" __stringify(GIC_PRIO_IRQON),
ARM64_HAS_IRQ_PRIO_MASKING)
: "=&r" (res)
: "r" ((int) flags)
: "memory");
return res;
}
- 调用函数arch_local_save_flags获取daif,保存在
flags
变量中,其中表示了cpu的irq情况, - 调用函数arch_irqs_disabled_flags对步骤一的返回值进行处理,只保留irq的bit。
2.2 raw_irqs_disabled
#define raw_local_irq_disable() arch_local_irq_disable()
static inline void arch_local_irq_disable(void)
{
if (system_has_prio_mask_debugging()) {
u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
}
asm volatile(ALTERNATIVE(
"msr daifset, #2 // arch_local_irq_disable",
__msr_s(SYS_ICC_PMR_EL1, "%0"),
ARM64_HAS_IRQ_PRIO_MASKING)
:
: "r" ((unsigned long) GIC_PRIO_IRQOFF)
: "memory");
}
raw_local_irq_disable主要是执行了汇编 “msr daifset, #2” ,从而达到关闭irq。
3.local_irq_save
#define local_irq_save(flags) do { raw_local_irq_save(flags); } while (0)
#define raw_local_irq_save(flags) \
do { \
typecheck(unsigned long, flags); \
flags = arch_local_irq_save(); \
} while (0)
static inline unsigned long arch_local_irq_save(void)
{
unsigned long flags;
flags = arch_local_save_flags();
/*
* There are too many states with IRQs disabled, just keep the current
* state if interrupts are already disabled/masked.
*/
if (!arch_irqs_disabled_flags(flags))
arch_local_irq_disable();
return flags;
}
3.1 arch_local_save_flags
保存中断状态,到flags里面,并返回flags
static inline unsigned long arch_local_save_flags(void)
{
unsigned long flags;
asm volatile(ALTERNATIVE(
"mrs %0, daif",
__mrs_s("%0", SYS_ICC_PMR_EL1),
ARM64_HAS_IRQ_PRIO_MASKING)
: "=&r" (flags)
:
: "memory");
return flags;
}
3.2 arch_local_irq_disable
检测如果未关闭中断,就通过arch_local_irq_disable关闭
static inline void arch_local_irq_disable(void)
{
if (system_has_prio_mask_debugging()) {
u32 pmr = read_sysreg_s(SYS_ICC_PMR_EL1);
WARN_ON_ONCE(pmr != GIC_PRIO_IRQON && pmr != GIC_PRIO_IRQOFF);
}
asm volatile(ALTERNATIVE(
"msr daifset, #2 // arch_local_irq_disable",
__msr_s(SYS_ICC_PMR_EL1, "%0"),
ARM64_HAS_IRQ_PRIO_MASKING)
:
: "r" ((unsigned long) GIC_PRIO_IRQOFF)
: "memory");
}
4.local_irq_restore
#define local_irq_restore(flags) do { raw_local_irq_restore(flags); } while (0)
#define raw_local_irq_restore(flags) \
do { \
typecheck(unsigned long, flags); \
arch_local_irq_restore(flags); \
} while (0)
4.1 arch_local_irq_restore
恢复之前保存的中断状态
static inline void arch_local_irq_restore(unsigned long flags)
{
asm volatile(ALTERNATIVE(
"msr daif, %0",
__msr_s(SYS_ICC_PMR_EL1, "%0"),
ARM64_HAS_IRQ_PRIO_MASKING)
:
: "r" (flags)
: "memory");
pmr_sync();
}