local_irq_enable,local_irq_disable(),local_irq_save和local_irq_restore函数

本文介绍了Linux ARM架构下用于中断处理的四个关键函数:local_irq_disable, local_irq_enable, local_irq_save 和 local_irq_restore。这些函数在进入和退出临界区时用于管理中断状态。local_irq_disable禁用中断,local_irq_enable启用中断,而local_irq_save和local_irq_restore则在保存中断状态的同时进行禁用和恢复。文章详细阐述了每个函数的内部实现和工作原理。" 108043407,9995035,PaddlePaddle手写数字识别实践:数据处理与模型构建,"['机器学习', '深度学习', '卷积神经网络', '数据处理', 'PaddlePaddle']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

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;
}
  1. 调用函数arch_local_save_flags获取daif,保存在 flags 变量中,其中表示了cpu的irq情况,
  2. 调用函数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();
}

`__do_softirq` 是一个内核函数,用于处理软中断(softirq)。下面是每一行的解释: ```c notrace void __do_softirq(void) ``` 这是函数的声明,表示这是一个无返回值的函数,同时也禁止对该函数进行跟踪。 ```c struct softirq_action *h; unsigned long flags; ``` 定义了两个变量,`h` 是一个指向 `softirq_action` 结构体的指针,`flags` 是一个无符号长整型数。 ```c local_irq_save(flags); ``` 保存当前中断状态并关闭中断。 ```c __local_bh_disable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET); ``` 禁用软中断。 ```c h = softirq_vec; ``` 将 `softirq_vec` 赋值给变量 `h`,`softirq_vec` 是一个 `softirq_action` 类型的数组,存储了系统支持的软中断处理函数。 ```c do { ``` 开始一个循环。 ```c struct softirq_action *this = READ_ONCE(*h); ``` 将 `h` 指向的地址处的值读取出来,赋值给变量 `this`。`READ_ONCE` 是一个宏,用于安全地读取一个变量的值。 ```c if (this) { ``` 判断 `this` 是否为空。 ```c this->action(this); ``` 如果不为空,则执行 `this` 中存储的函数指针,也就是调用该软中断的处理函数。 ```c } else { ``` 如果为空,则说明该软中断处理函数已经被移除,跳过本次循环。 ```c h++; ``` 指向下一个 `softirq_action` 结构体。 ```c } while (this); ``` 循环直到 `this` 为空。 ```c local_bh_enable(); ``` 启用软中断。 ```c local_irq_restore(flags); ``` 恢复原来的中断状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值