CPU的NMI中断常用作Hard lock检测。无论CPU是否lock,硬件始终要保证NMI中断能够被响应。作为Hard lock检测的方法,当CPU硬件锁死后,其时钟中断可能无法被响应,导致时钟计数值无法变化。

在NMI中断处理时,通过判断当前时钟计数值是否与前一次NMI中断的时钟计数值相同来判断CPU硬件死锁。该过程可以用下图表示:

一. 时钟计数值的处理方式

对于X86,当时钟中断来临时,kernel都会在中断上文中对时钟计数器+1。在低分辨率模式时,计数器+1的处理过程如下:

//arch/x86/kernel/time_32.c
irqreturn_t timer_interrupt(int irq, void *dev_id)
{/* Keep nmi watchdog up to date */per_cpu(irq_stat, smp_processor_id()).irq0_irqs++;//...
}

开启高分辨率模式后,在0xef本地APIC中断处理中对时钟计数值+1,其处理如下:

//arch/x86/kernel/apic_32.c
/* The guts of the apic timer interrupt */
static void local_apic_timer_interrupt(void)
{int cpu = smp_processor_id();struct clock_event_device *evt = &per_cpu(lapic_events, cpu);//....per_cpu(irq_stat, cpu).apic_timer_irqs++;  //计数值+1evt->event_handler(evt);
}

二. NMI中断的处理

NMI中断号为2,其中断门在trap_init函数中设置,中断入口函数是nmi。该函数是汇编函数,定义在 arch/x86/kernel/entry_32.S中(为了突出重要部分,其中省略了部分内容) :

/* arch/x86/kernel/entry_32.S */
/* NMI is doubly nasty. It can happen _while_ we're handling* a debug fault, and the debug fault hasn't yet been able to* clear up the stack. So we first check whether we got  an* NMI on the sysenter entry path, but after that we need to* check whether we got an NMI on the debug path where the debug* fault happened on the sysenter path.*/
KPROBE_ENTRY(nmi)RING0_INT_FRAMEpushl %eaxCFI_ADJUST_CFA_OFFSET 4...je nmi_debug_stack_check
nmi_stack_correct:/* We have a RING0_INT_FRAME here */pushl %eax                           #压入中断号CFI_ADJUST_CFA_OFFSET 4SAVE_ALL                             #保护寄存器环境xorl %edx,%edx     # zero error code  设置函数实参error_codemovl %esp,%eax       # pt_regs pointer  设置函数实参regscall do_nmi             #进入中断处理函数jmp restore_nocheck_notraceCFI_ENDPROCnmi_stack_fixup:RING0_INT_FRAMEFIX_STACK(12,nmi_stack_correct, 1)jmp nmi_stack_correct...
1:  INTERRUPT_RETURNCFI_ENDPROC
.section __ex_table,"a".align 4.long 1b,iret_exc
.previous
KPROBE_END(nmi)

NMI中断处理入口首先进行一些状态寄存器更改和栈处理。然后将中断号从eax入栈,将函数实参写入edx和eax,调用do_nmi函数开始处理,do_nmi函数定义如下:

//arch/x86/kernel/traps_32.c
fastcall __kprobes void do_nmi(struct pt_regs * regs, long error_code)
{int cpu;nmi_enter();cpu = smp_processor_id();++nmi_count(cpu);if (!ignore_nmis)default_do_nmi(regs);  //开始处理nmi_exit();
}

do_nmi函数没什么可说明的,我们感兴趣其中的default_do_nmi函数,该函数定义如下:

//arch/x86/kernel/traps_32.c
static __kprobes void default_do_nmi(struct pt_regs * regs)
{unsigned char reason = 0;/* Only the BSP gets external NMIs from the system.  */if (!smp_processor_id())reason = get_nmi_reason();if (!(reason & 0xc0)) {if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 2, SIGINT)== NOTIFY_STOP)return;
#ifdef CONFIG_X86_LOCAL_APIC/* Ok, so this is none of the documented NMI sources,* so it must be the NMI watchdog. */if (nmi_watchdog_tick(regs, reason))  //开始判断是否hard lockreturn;if (!do_nmi_callback(regs, smp_processor_id()))
#endifunknown_nmi_error(reason, regs);return;}if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP)return;if (reason & 0x80)mem_parity_error(reason, regs);if (reason & 0x40)io_check_error(reason, regs);/* Reassert NMI in case it became active meanwhile* as it's edge-triggered. */reassert_nmi();
}

该函数在配置LOCAL_APIC后,调用nmi_watchdog_tick,该函数是判断hard lock的关键函数,直接进入函数定义:


__kprobes int nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
{/* Since current_thread_info()-> is always on the stack, and we* always switch the stack NMI-atomically, it's safe to use* smp_processor_id(). */unsigned int sum;int touched = 0;int cpu = smp_processor_id();int rc=0;...if (cpu_isset(cpu, backtrace_mask)) {  //backtrace跟踪...}/* Take the local apic timer and PIT/HPET into account. We don't* know which one is active, when we have highres/dyntick on */sum = per_cpu(irq_stat, cpu).apic_timer_irqs +per_cpu(irq_stat, cpu).irq0_irqs;/* if the none of the timers isn't firing, this cpu isn't doing much */if (!touched && last_irq_sums[cpu] == sum) {/** Ayiee, looks like this CPU is stuck ...* wait a few IRQs (5 seconds) before doing the oops ...*/alert_counter[cpu]++;if (alert_counter[cpu] == 5*nmi_hz)/* die_nmi will return ONLY if NOTIFY_STOP happens..*/die_nmi(regs, "BUG: NMI Watchdog detected LOCKUP");} else {last_irq_sums[cpu] = sum;alert_counter[cpu] = 0;}.....return rc;
}

在该函数内,首先使用notify_die(DIE_NMI)从MSR读取当前CPU的模式,再判断时钟计数器,这里使用了一个技巧:始终将低分辨率时钟计数值+高分辨率时钟计数值作为时钟计数值,无论是否开启高分辨时钟模式,该值一定反映时钟计数的变化。

若相邻两次NMI的时钟计数值不变,此时即判断发生了hard lock,进一步确认,kernel等待5s,若5s内该状况一直保持不变,则进入die_nmi执行oops。否则更新上一次时钟计数值last_irq_sum,至此hard lock判断完毕。

参考文档:

https://www.cnblogs.com/muahao/p/7595158.html  (解释hard lock和soft lock)

《Professional Linux Kernel Architecture》 chapter14.1

Intel Intel 64 and lA-32 Architectures Software Developer's Manual

解析kernel 2.6.24使用NMI中断对Hard lock的处理相关推荐

  1. nmi中断配置_外部中断NMI中断.ppt

    外部中断NMI中断.ppt * 1.先看其引脚结构再看功能1. 可编程的含义:通过对芯片编程,使芯片实现不同的功能. 中断比较多的情况下,使用中断控制器来管理中断. 用来管理系统的硬件中断. * 1. ...

  2. nmi中断配置_关于单片机中的NMI_Handler(不可屏蔽中断处理器)

    该博客文章以MKL15Z4系列单片机为例,参考文档均来自NXP官方提供的芯片用户手册和数据手册:所使用的的开发环境是MCUXpresso. 一.简单的介绍 首先请看两张截图: 一张是截取于单片机工程的 ...

  3. linux内核nmi中断,LINUX AM335X NMI中断使用方法

    首先高清楚你要用什么中断,如果是用普通的GPIO去申请中断,那在网上百度就可以了,这里主要讲一下如何使用NMI中断(不可屏蔽中断),自己在这里走了很多弯路,希望可以帮助大家理解,有其他看法的大家在这里 ...

  4. STM32Gx系列进入NMI中断造成芯片死机的解决办法

    STM32Gx系列进入NMI中断造成芯片死机的解决办法 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 STM32Gx系列进入NMI中断造成芯片死机的解决办法 前言 一.问 ...

  5. nmi中断配置_C6748 NMI不可屏蔽中断和GPIO

    http://blog.csdn.net/zengaliang/article/details/78705019 1.主函数流程 此程序的作用是实现不可屏蔽中断功能.NMI(NonMaskableIn ...

  6. NMI watchdog: BUG: soft lockup - CPU#2 stuck for 23s!

    <NMI watchdog: BUG: soft lockup> <kernel:NMI watchdog: BUG: soft lockup - CPU#6 stuck for 2 ...

  7. Linux Watchdog 机制

    ​前言 Watchdog 是 Linux 系统一个很重要的机制,其目的是监测系统运行的情况,一旦出现锁死,死机的情况,能及时重启机器(取决于设置策略),并收集crash dump. watchdog, ...

  8. Linux系列之soft lockup机制 浅析

    Linux系列之soft lockup机制 浅析 1.背景 2.什么是lockup? 2.1 lockup检测机制 2.2 softlockup的工作原理 3.soft lockup机制分析 3.1 ...

  9. linux 内核笔记之watchdog

    一.概要 watchdog简而言之,watchdog是为了保证系统正常运行,或者从死循环,死锁等一场状态退出的一种机制. 看门狗分硬件看门狗和软件看门狗.硬件看门狗是利用一个定时器电路,其定时输出连接 ...

最新文章

  1. 网络工程师课程---4、网络层(网关是什么)
  2. AI规模化落地,英特尔至强的七重助力
  3. Linus:Linux太烂,我要删了它,你们用Windows XP吧!
  4. 线上会议丨中国中文信息学会2020学术年会将于12月27日举行
  5. Linux下 Nginx 启动 重启 关闭
  6. 如何把安静的程序员逼成话唠
  7. And(CF-1013B)
  8. 连接两个std :: vector
  9. 作业 3 应用分支与循环结构解决问题 计算分段函数的值
  10. Matlab toolbox Manopt流形优化工具包介绍
  11. 2021年中国上市公司发明授权数量及分布:发明授权数量连续5年增长,广东省位居全国第一[图]
  12. 个人用游戏设计框架图
  13. 我国超级计算机的发展成就,中国最近的科技发展成就
  14. 字符串的练习 统计字符串中大写,小写,数字的个数 29
  15. Qt(c++)调用python一直报错slot、hypot等
  16. android小部件的作用,Android 应用小部件的实现
  17. 树莓派安装安装fcitx及google拼音输入法
  18. MATLAB模糊控制工具箱的使用及常见问题处理
  19. C/C++黑魔法-编译期运行的sizeof
  20. 玩转STM32F0 Value Line Discovery 之 时钟配置工具

热门文章

  1. 在Win10系统上使用VScode + Cmake配置C/C++开发环境,实现一键编译运行
  2. Prompt 学习和微调综述 (Prompt Learning and Tuning)
  3. Cotex-M0中断及与Cotex-M3的对比
  4. JSP程序设计实训(十二)——MVC设计模式
  5. 航空公司管理.C语言
  6. 【gromacs学习】-gromacs生成的xvg文件处理
  7. window.scroll 浏览器滚动条的参数总结..........
  8. Nasm汇编GDB调试
  9. 联想ThinkPad E40新机磁盘分区问题
  10. android 多条件搜索界面,SearchView实现搜索功能