13、禁止/开启中断实现分析
软件可以禁止中断,使处理器不响应所有中断请求,但是不可屏蔽中断(Non Maskable Interrupt,NMI)是个例外。
一、接口
1.1、禁止中断的接口
- local_irq_disable() 直接关闭当前CPU的中断功能,之后这个CPU不会再响应任何中断请求。
- local_irq_save(flags) 先记住当前CPU的中断开关状态(保存到变量flags里),然后再关闭中断。这样后续可以通过其他函数恢复到保存前的状态。
关键点:
- 只对当前运行的CPU有效,其他CPU的中断不会受影响
- 关闭后,当前CPU会完全忽略所有中断信号
ARM64架构禁止中断的函数local_irq_disable()如下:
C
local_irq_disable() -> raw_local_irq_disable() -> arch_local_irq_disable()arch/arm64/include/asm/irqflags.h
static inline void arch_local_irq_disable(void)
{
asm volatile(
"msr daifset, #2 // arch_local_irq_disable"
:
:
: "memory");
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
把处理器状态的中断掩码位设置成1,从此以后处理器不会响应中断请求。
1.2、开启中断
ARM64架构中控制中断的函数说明:
开启中断的直接方法:
local_irq_enable():立即打开当前处理器的中断功能。
保存和恢复中断状态的成对函数:
local_irq_restore(flags):用之前保存的flags值恢复当前处理器的中断状态(开启或关闭)。
关键使用规则:
- 直接开关函数有局限:不能重复使用local_irq_disable()关闭后再多次使用local_irq_enable()开启,这样会导致不可预测的结果。
- 安全的嵌套方法:使用local_irq_save(flags)保存状态并获取标志后,可以多次嵌套调用该函数和对应的local_irq_restore(flags)恢复,系统能正确处理多次保存和恢复的层级关系。
简单总结:直接开关函数像简单的灯开关,按一下开/关,不能连续多次操作;而保存/恢复函数像带记忆功能的开关,能记住每次操作前的状态,支持复杂场景下的安全使用。:
C
local_irq_enable() -> raw_local_irq_enable() -> arch_local_irq_enable()arch/arm64/include/asm/irqflags.h
static inline void arch_local_irq_enable(void)
{
asm volatile(
"msr daifclr, #2 // arch_local_irq_enable"
:
:
: "memory");
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
把处理器状态的中断掩码位设置成0。
二、禁止/开启单个中断
以下是更直白的改写版本:
在ARM64架构的GIC中断控制器中:
- 如果要开启中断n:
- 调用
enable_irq(n)
函数 - 这会设置GICD_ISENABLERn寄存器
- 让控制器允许该中断传递到CPU
- 如果要关闭中断n:
- 调用
disable_irq(n)
函数 - 这会设置GICD_ICENABLERn寄存器
- 让控制器屏蔽该中断
简单来说:
- 函数参数n就是你要控制的中断号
- 开启时:写入ISENABLER寄存器 → 允许中断传递
- 关闭时:写入ICENABLER寄存器 → 阻断中断传递
- 只有当开启状态存在时,设备的中断信号才会被CPU接收处理
三、内核使用案例
input/touchscreen/atmel_mxt_ts.c
C
static int mxt_acquire_irq(struct mxt_data *data)
{
int error;
enable_irq(data->irq);
if (data->use_retrigen_workaround) {
error = mxt_process_messages_until_invalid(data);
if (error)
return error;
}
return 0;
}
static int mxt_soft_reset(struct mxt_data *data)
{
struct device *dev = &data->client->dev;
int ret = 0;
dev_info(dev, "Resetting device\n");
disable_irq(data->irq);
reinit_completion(&data->reset_completion);
ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false);
if (ret)
return ret;
/* Ignore CHG line for 100ms after reset */
msleep(MXT_RESET_INVALID_CHG);
mxt_acquire_irq(data);
ret = mxt_wait_for_completion(data, &data->reset_completion,
MXT_RESET_TIMEOUT);
if (ret)
return ret;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42