在NMI中调用preempt_enable()的影响及代码实现合理性问询
关于NMI中调用preempt_enable()的两个问题解答
问题1:在NMI(不可屏蔽中断)内部调用preempt_enable()会发生什么?
- NMI处理上下文属于原子上下文:进入NMI handler时,内核会自动提升
preempt_count的NMI计数位,同时硬件会关闭本地中断。这种情况下,即便调用preempt_enable()降低抢占计数,内核抢占机制也会被严格禁止——原子上下文不允许进程调度切换。 - 如果调用
preempt_enable()前先执行了local_irq_enable()开启本地中断,此时普通可屏蔽中断可以响应,但进程调度依然不会触发:调度器会检查in_nmi()标志,发现处于NMI上下文时直接跳过调度逻辑。 - 若调用
preempt_enable()后没有对应的preempt_disable()来平衡计数,会导致后续抢占计数异常,可能触发内核BUG(比如后续进入可抢占上下文时错误允许抢占,引发不可预期的调度)。
问题2:给定代码中在nmi_exit()之前调用preempt_enable()是否会引发问题?
从代码逻辑和内核上下文规则来看,这段实现是安全无问题的,具体原因如下:
- 抢占计数严格平衡:代码里
preempt_enable()和preempt_disable()是成对出现的,处理完用户态MCE逻辑后会将抢占计数恢复到初始状态,不会留下异常状态影响后续流程。 - NMI上下文的调度保护:即便开启了抢占,当前仍处于NMI上下文(
nmi_exit()未调用,in_nmi()标志为真),调度器会直接跳过调度操作,不会发生进程切换,完全不会破坏NMI处理的原子性。 - 操作场景的安全性:代码通过
BUG_ON提前验证了当前处于用户态线程栈,所有针对current进程的字段修改、信号发送操作都是针对当前CPU的活跃进程,不存在并发修改风险;开启本地中断只是允许普通可屏蔽中断响应,不会干扰NMI本身的处理。
这段代码的设计意图很明确:在用户态MCE的处理流程中,临时允许中断响应和正常的抢占计数状态,以便执行do_memory_failure()这类可能依赖部分内核正常上下文的操作,之后通过成对的禁用操作恢复NMI的原子处理状态,最后调用nmi_exit()退出NMI上下文。
内容的提问来源于stack exchange,提问作者Mark Kang
相关产品推荐
相关产品推荐

