Bootstrap

关于linux kernel hardlockup 的探究

1. 基本解释

Hardlockup(硬锁)主要是由于 中断被禁用中断被屏蔽过长时间中断无法处理 导致的。
硬锁的本质是 CPU 核心长时间不响应外部事件. 包括中断,调度等。

2. 驱动模拟hardlockup

以下为代码实现

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>

static int __init hardlockup_test_init(void)
{
    printk(KERN_INFO "Simulating hard lockup...\n");

    // 禁用当前 CPU 的中断
    local_irq_disable();
	/*
	* 
	*/
    // 进入一个无限循环,模拟死锁状态
    while (1) {
        // 通过空循环让系统不响应任何中断
        cpu_relax();  // 允许 CPU 做一些优化
    }

#if 0
	// 模拟长时间阻塞的 I/O 操作,无法被中断打断
    msleep(100000);  // 阻塞100秒

    // 恢复中断
    local_irq_enable();
#endif	
    return 0;
}

static void __exit hardlockup_test_exit(void)
{
    // 启用中断
    local_irq_enable();
    printk(KERN_INFO "Hard lockup simulation exit\n");
}

module_init(hardlockup_test_init);
module_exit(hardlockup_test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Qing");
MODULE_DESCRIPTION("Simulate Hard Lockup for NMI Watchdog Test");

驱动中,local_irq_disable() 会关闭 该task 对应的CPU 中断。while 循环保证这个task 一直忙等待,永远不会退出。这样,这个CPU 核就永远无法响应中断,系统也不会产生调度。 这时就发生了著名的 hardlockup
我们知道,此时普通的中断是无法响应的,但是可以响应NMI 中断,因为NMI 中断是不可屏蔽,且优先级最高的。
大部分Linux 操作系统,都默认使能了NMI Watchdog, 它其实是一个定时器,到达一定时间,产生一次NMI 中断, 在该中断处理程序中,会检查各个CPU 是不是处于hardlockup 或者softlockup 状态,并产生内核异常打印,或这重启等动作。

2.1 关于linux调度器

Linux 调度器依赖中断来切换任务。通常,中断(尤其是时钟中断)是调度任务的关键机制. 在定时器中断处理程序中,调度器会判断当前任务是否需要被抢占。如果是,就会触发任务切换。一旦中断被关闭,定时器不能发生中断,调度程序也无法运行。

  • local_irq_disable() 禁用了中断后
  • 时钟中断不会触发。
  • 调度器无法运行。
  • 当前任务一直运行,不会被其他任务抢占。

3. 异常log

kernel: [1299142.682506] NMI watchdog: Watchdog detected hard LOCKUP on cpu 10
kernel: [1299142.682507] Modules linked in: hardlock(O+) ......
.......
kernel: [1299142.682522] RIP: 0010:hardlockup_test_init+0x1e/0x1000 [hardlock]
kernel: [1299142.682522] Code: Bad RIP value.
kernel: [1299142.682522] RSP: 0018:ffffaf9bc9c4fc60 EFLAGS: 00000086
kernel: [1299142.682522] RAX: 0000000000000019 RBX: 0000000000000000 RCX: 0000000000000000
kernel: [1299142.682523] RDX: 0000000000000000 RSI: ffff92390c29c8c8 RDI: ffff92390c29c8c8
kernel: [1299142.682523] RBP: ffffaf9bc9c4fc60 R08: 00000000000005eb R09: 0000000000000004
kernel: [1299142.682523] R10: 0000000000000000 R11: 0000000000000001 R12: ffffffffc0b7d000
kernel: [1299142.682523] R13: ffff92366a471300 R14: ffffaf9bc9c4fe68 R15: ffffffffc1307000
kernel: [1299142.682524] FS:  00007fd867858540(0000) GS:ffff92390c280000(0000) knlGS:0000000000000000
kernel: [1299142.682524] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
kernel: [1299142.682524] CR2: ffffffffc0b7cff4 CR3: 0000000100494004 CR4: 00000000007606e0
kernel: [1299142.682524] PKRU: 55555554
kernel: [1299142.682524] Call Trace:
kernel: [1299142.682524]  do_one_initcall+0x4a/0x210
kernel: [1299142.682525]  ? _cond_resched+0x19/0x40
kernel: [1299142.682525]  ? kmem_cache_alloc_trace+0x170/0x230
kernel: [1299142.682525]  do_init_module+0x4f/0x20f
kernel: [1299142.682525]  load_module+0x1e77/0x22f0
kernel: [1299142.682525]  __do_sys_finit_module+0xfc/0x120
kernel: [1299142.682525]  ? __do_sys_finit_module+0xfc/0x120
kernel: [1299142.682526]  __x64_sys_finit_module+0x1a/0x20
kernel: [1299142.682526]  do_syscall_64+0x57/0x190
kernel: [1299142.682526]  entry_SYSCALL_64_after_hwframe+0x5c/0xc1

4. hardlockup 如何检测

NMI Watchdog会检测到hardlockup. 因为NMI 中断不可屏蔽!

5. 生成kdump 转储文件(可选)

这个会导致CPU 卡住,不会奔溃,要让发生hardlockup 时,产生奔溃,需要是能系统的以下参数:

echo 1 > /proc/sys/kernel/hardlockup_panic

再结合kdump 就可以生成内核转储文件了。

;